#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mpt3sas_base.h" #if defined(TARGET_MODE) #include "mpt3sas_stm.h" //MODULE_LICENSE("Proprietary"); static int initialize_target_mode = -1; module_param(initialize_target_mode, int, S_IWUSR); MODULE_PARM_DESC(initialize_target_mode, " intialize controller with " "target mode support (target mode) "); static int ignore_lun_zero = -1; module_param(ignore_lun_zero, int, S_IWUSR); MODULE_PARM_DESC(ignore_lun_zero, " ignore lun zero (target mode) "); static int busy_emu = 0; module_param(busy_emu, int, S_IWUSR); MODULE_PARM_DESC(busy_emu, " busy emulation (target mode) "); static int num_luns = DEFAULT_NUM_LUNS; module_param(num_luns, int, S_IWUSR); MODULE_PARM_DESC(num_luns, " max number luns (target mode) "); static int num_blocks = -1; module_param(num_blocks, int, S_IWUSR); MODULE_PARM_DESC(num_blocks, " number blocks per lun, " "range 32 to 4194304 default=2048 (target mode)"); static char mpt_stm_hostname[16+1]; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) static void _initiator_missing_event(struct work_struct *work); #else static void _initiator_missing_event(void *arg); #endif /* target assist timeout */ #define STM_TARGET_ASSIST_TIMEOUT (65*HZ) /* 65 seconds */ #define STM_TARGET_ASSIST_STATUS_TIMEOUT (15*HZ) /* 15 seconds */ /* task managment timeout */ #define STM_TM_TIMEOUT (15*HZ) /* 15 seconds */ /* command post timeout */ #define STM_CMP_POST_TIMEOUT (15*HZ) /* 15 seconds */ #if defined(STM_RING_BUFFER) /* The following stm_rb_xxx routines are used for maintaining a ring buffer. * We will be tracking communication between driver and firmware. * * To dump the ring buffer you do the following. * echo 2 > /sys/class/scsi_host/host#/stm * * The ring buffer depth is defined in STM_RB_MAXSIZE in the _stm.h file. */ static struct stm_rb_element * get_next_element(struct MPT_STM_PRIV *priv) { struct stm_rb_element *rb; priv->rb_head = (priv->rb_head == (STM_RB_MAXSIZE - 1)) ? 0 : priv->rb_head + 1; rb = &priv->ring_buffer[priv->rb_head]; return rb; } /* add entry ~ command buffers */ static void stm_rb_cmd_buffer(struct MPT_STM_PRIV *priv, u16 io_index, u8 frame_type, u8 opcode, u16 tag) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_CMD_BUFFER; rb->io_index = io_index; rb->request_info.a.frame_type = frame_type; rb->request_info.a.opcode = opcode; rb->tag = tag; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ target assist with data */ static void stm_rb_ta_data(struct MPT_STM_PRIV *priv, u16 io_index, u16 tag, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_TA_DATA; rb->io_index = io_index; rb->request_info.smid = smid; rb->tag = tag; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ target assist with status */ static void stm_rb_ta_status(struct MPT_STM_PRIV *priv, u16 io_index, u16 tag, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_TA_STATUS; rb->io_index = io_index; rb->request_info.smid = smid; rb->tag = tag; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ target assist succesfull reply */ static void stm_rb_ta_reply_success(struct MPT_STM_PRIV *priv, u16 io_index, u16 tag, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_TA_REPLY_SUCCESS; rb->io_index = io_index; rb->request_info.smid = smid; rb->tag = tag; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ target assist error reply */ static void stm_rb_ta_reply_error(struct MPT_STM_PRIV *priv, u16 io_index, u16 tag, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_TA_REPLY_ERROR; rb->io_index = io_index; rb->request_info.smid = smid; rb->tag = tag; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ abort request */ static void stm_rb_abort_request(struct MPT_STM_PRIV *priv, u16 io_index_to_abort, u16 mid_to_abort, u16 smid, u8 abort_type) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_ABORT_REQ; rb->io_index = io_index_to_abort; rb->request_info.smid = smid; rb->abort_type = abort_type; rb->tag = mid_to_abort; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ abort reply */ static void stm_rb_abort_reply(struct MPT_STM_PRIV *priv, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_ABORT_REPLY; rb->request_info.smid = smid; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ post all reply */ static void stm_rb_post_all_reply(struct MPT_STM_PRIV *priv, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_POST_ALL_REPLY; rb->request_info.smid = smid; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ post single command buffer */ static void stm_rb_post_single(struct MPT_STM_PRIV *priv, u16 io_index, u16 tag, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_POST_SINGLE; rb->io_index = io_index; rb->request_info.smid = smid; rb->tag = tag; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* add entry ~ post all command buffer */ static void stm_rb_post_all(struct MPT_STM_PRIV *priv, u16 smid) { unsigned long flags; struct stm_rb_element *rb; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb = get_next_element(priv); memset(rb, 0, sizeof(*rb)); rb->type = STM_RB_POST_ALL; rb->request_info.smid = smid; spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } static struct { u8 type; char *name; } type_levels[] = { { STM_RB_CMD_BUFFER, "cmd_buffer " }, { STM_RB_TA_DATA, "ta_data " }, { STM_RB_TA_STATUS, "ta_status " }, { STM_RB_TA_REPLY_SUCCESS, "ta_success " }, { STM_RB_TA_REPLY_ERROR, "ta_error " }, { STM_RB_ABORT_REQ, "abort_req " }, { STM_RB_ABORT_REPLY, "abort_reply " }, { STM_RB_POST_SINGLE, "post_single " }, { STM_RB_POST_ALL, "post_all " }, { STM_RB_POST_ALL_REPLY, "post_all_reply" }, }; static const char *type_level_name(u8 type) { int i; for (i = 0; i < ARRAY_SIZE(type_levels); i++) if (type_levels[i].type == type) return type_levels[i].name; return NULL; } static struct { u8 type; char *name; } iu_levels[] = { { SSP_CMD_FRAME, "ssp" }, { SSP_TASK_FRAME, "task" }, }; static const char *iu_level_name(u8 type) { int i; for (i = 0; i < ARRAY_SIZE(iu_levels); i++) if (iu_levels[i].type == type) return iu_levels[i].name; return NULL; } static void dump_ring_buffer(struct stm_rb_element *ring_buffer) { printk("index_%04d\t%s\t", ring_buffer->io_index, type_level_name(ring_buffer->type)); switch (ring_buffer->type) { case STM_RB_CMD_BUFFER: printk("%s/0x%02x\ttag(0x%04x)\n", iu_level_name(ring_buffer->request_info.a.frame_type), ring_buffer->request_info.a.opcode, ring_buffer->tag); break; case STM_RB_TA_DATA: case STM_RB_TA_STATUS: case STM_RB_TA_REPLY_SUCCESS: case STM_RB_TA_REPLY_ERROR: case STM_RB_POST_SINGLE: printk("smid(%04d)\ttag(0x%04x)\n", ring_buffer->request_info.smid, ring_buffer->tag); break; case STM_RB_ABORT_REQ: printk("abort_type(0x%02x)\t" "mid_to_abort(0x%04x)\tsmid(0x%04x)\n", ring_buffer->abort_type, ring_buffer->tag, ring_buffer->request_info.smid); break; case STM_RB_POST_ALL_REPLY: case STM_RB_ABORT_REPLY: case STM_RB_POST_ALL: printk("smid(%04d)\n", ring_buffer->request_info.smid); break; break; } } void sysfs_dump_ring_buffer(struct MPT_STM_PRIV *priv) { struct stm_rb_element *ring_buffer; unsigned long flags; int i; u16 rb_index; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); rb_index = (priv->rb_head + 1 == (STM_RB_MAXSIZE) ? 0 : priv->rb_head + 1); for (i = rb_index, ring_buffer = &priv->ring_buffer[rb_index]; i < STM_RB_MAXSIZE ; i++, ring_buffer++) if (ring_buffer->type) dump_ring_buffer(ring_buffer); for (i = 0, ring_buffer = priv->ring_buffer; i <= priv->rb_head ; i++, ring_buffer++) if (ring_buffer->type) dump_ring_buffer(ring_buffer); spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } /* The following couple routines are used for dumping the state * of each command buffer state. * * To dump the state info you do the following. * echo 1 > /sys/class/scsi_host/host#/stm * */ static void dump_kernel_thread_state(struct CMD *io_cmd) { Mpi2TargetSspCmdBuffer * ssp_iu; Mpi2TargetSspTaskBuffer *task_iu; printk("index_%04d state(0x%04x) status(0x%02x) smid(%04d) lun(%d) ", io_cmd->io_index, io_cmd->io_state, io_cmd->status, io_cmd->smid, (int) io_cmd->lun); switch (io_cmd->cmd[0]) { case SSP_CMD_FRAME: ssp_iu = (Mpi2TargetSspCmdBuffer *)io_cmd->cmd; printk("%s/0x%02x tag(0x%04x)\n", iu_level_name(SSP_CMD_FRAME), ssp_iu->CDB[0], be16_to_cpu(ssp_iu->Tag)); break; case SSP_TASK_FRAME: task_iu = (Mpi2TargetSspTaskBuffer *)io_cmd->cmd; printk("%s/0x%02x tag(0x%04x)\n", iu_level_name(SSP_TASK_FRAME), be16_to_cpu(task_iu->TaskManagementFunction), be16_to_cpu(task_iu->ManagedTaskTag)); break; default: printk("\n"); break; } } void sysfs_dump_kernel_thread_state(struct MPT_STM_PRIV *priv) { struct CMD *io_cmd; unsigned long flags; int i; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); io_cmd = priv->cmd_buffer; for (i = 0; i < priv->num_cmd_buffers; i++, io_cmd++) dump_kernel_thread_state(io_cmd); spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); } #endif /* STM_RING_BUFFER */ /** * u8 stm_io_cb_idx - stm callback index for sending internal commands to * firmware * * 1) The common API for obtaining a smid (where smid is system message id) is: * smid = mpt3sas_base_get_smid(ioc, ioc->stm_io_cb_idx); * * 2) The common API for obtaining the request message frame contents is: * mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); * * 3) The command API for sending the request to firmware is: * sending a config page use this: * mpt3sas_base_put_smid_default(ioc, smid); * completion is in this function: * _stm_io_done(ioc, smid, msix_index, reply); * * sending a target assist request: * mpt3sas_base_put_smid_target_assist(ioc, smid, io_index); * completion is in this function: * _stm_io_done(ioc, smid, msix_index, reply); * completion when successfull: * stm_target_assist_success_reply(ioc, smid, msix_index, io_index, * sequence_number); * */ static u8 stm_io_cb_idx = -1; /* normal io */ static u8 stm_tm_cb_idx = -1; /* task managment */ static u8 stm_tm_imm_cb_idx = -1; /* task managment immediate */ static u8 stm_post_cb_idx = -1; /* post all command buffers */ /** * _stm_set_io_state - set io_state * @io_cmd: per command private data * @io_state: cmd buffer state */ static inline void _stm_set_io_state(struct CMD *io_cmd, u16 io_state) { unsigned long flags; spin_lock_irqsave(&io_cmd->io_state_lock, flags); io_cmd->io_state |= io_state; spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); } /** * _stm_clear_io_state - clear io_state * @io_cmd: per command private data * @io_state: cmd buffer state */ static inline void _stm_clear_io_state(struct CMD *io_cmd, u16 io_state) { unsigned long flags; spin_lock_irqsave(&io_cmd->io_state_lock, flags); io_cmd->io_state &= ~io_state; spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); } /** * _stm_check_io_state - check io_state * @io_cmd: per command private data * @io_state: cmd buffer state * * return 1 if bitwise io_state bit is set */ static inline u8 _stm_check_io_state(struct CMD *io_cmd, u16 io_state) { unsigned long flags; u8 rc; spin_lock_irqsave(&io_cmd->io_state_lock, flags); rc = (io_cmd->io_state & io_state) ? 1 : 0; spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); return rc; } /** * _stm_set_status - set status * @io_cmd: per command private data * @io_state: cmd buffer state */ static inline void _stm_set_status(struct CMD *io_cmd, u16 status) { unsigned long flags; spin_lock_irqsave(&io_cmd->status_lock, flags); io_cmd->status |= status; spin_unlock_irqrestore(&io_cmd->status_lock, flags); } /** * _stm_set_status_pending - set status to MPT3_CMD_PENDING * @io_cmd: per command private data * * Return 0 if success else error */ static inline int _stm_set_status_pending(struct CMD *io_cmd) { unsigned long flags; int rc = 0; spin_lock_irqsave(&io_cmd->status_lock, flags); if (unlikely(io_cmd->io_state & IO_STATE_ABORTED)) rc = -EAGAIN; else io_cmd->status = MPT3_CMD_PENDING; spin_unlock_irqrestore(&io_cmd->status_lock, flags); return rc; } /** * _stm_set_status_not_used - set status to MPT3_CMD_NOT_USED * @io_cmd: per command private data */ static inline void _stm_set_status_not_used(struct CMD *io_cmd) { unsigned long flags; spin_lock_irqsave(&io_cmd->status_lock, flags); io_cmd->status = MPT3_CMD_NOT_USED; spin_unlock_irqrestore(&io_cmd->status_lock, flags); } /** * _stm_clear_status - clear status * @io_cmd: per command private data * @io_state: cmd buffer state */ static inline void _stm_clear_status(struct CMD *io_cmd, u16 status) { unsigned long flags; spin_lock_irqsave(&io_cmd->status_lock, flags); io_cmd->status &= ~status; spin_unlock_irqrestore(&io_cmd->status_lock, flags); } /** * _stm_check_status - check status * @io_cmd: per command private data * @io_state: cmd buffer state * * return 1 if bitwise io_state bit is set */ static inline u8 _stm_check_status(struct CMD *io_cmd, u16 status) { unsigned long flags; u8 rc; spin_lock_irqsave(&io_cmd->status_lock, flags); rc = (io_cmd->status & status) ? 1 : 0; spin_unlock_irqrestore(&io_cmd->status_lock, flags); return rc; } /** * _stm_wait_timeout - timer expiry handling for busy emulation * @__data: per command private data * */ static void _stm_wait_timeout(unsigned long __data) { struct MPT_STM_PRIV *priv = (struct MPT_STM_PRIV *)__data; printk(KERN_INFO "timer expired!!\n"); priv->return_busy_sense = 0; } /** * mpt3sas_stm_zero_smid_handler - callback hook when smid equals zero * @ioc: pointer to scsi command object * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt * * We snooping reply's with zero smid. It might be meant for us. * * Return 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function. */ u8 mpt3sas_stm_zero_smid_handler(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; struct MPT_STM_PRIV *priv = ioc->priv; mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) return 1; switch (mpi_reply->Function) { case MPI2_FUNCTION_EVENT_NOTIFICATION: { Mpi2EventNotificationReply_t *en_reply = (Mpi2EventNotificationReply_t *)mpi_reply; u16 event = le16_to_cpu(en_reply->Event); switch (event) { case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE: { struct change_event *init_dev_event; Mpi2EventDataSasInitDevStatusChange_t *event_data = (Mpi2EventDataSasInitDevStatusChange_t *) &en_reply->EventData; if (event_data->ReasonCode == MPI2_EVENT_SAS_INIT_RC_ADDED) { if (!busy_emu) break; /* Code below for emulating busy, it will * return BUSY status for 15 seconds * * This will help with testing initator. */ printk(MPT3SAS_INFO_FMT "adding timer\n", ioc->stm_name); priv->return_busy_sense = 1; init_timer(&priv->return_busy_sense_timer); priv->return_busy_sense_timer.expires = jiffies + (15 * HZ); priv->return_busy_sense_timer.data = (unsigned long)priv; priv->return_busy_sense_timer.function = _stm_wait_timeout; add_timer(&priv->return_busy_sense_timer); break; } if (priv->return_busy_sense == 1) { printk(MPT3SAS_INFO_FMT "deleting timer\n", ioc->stm_name); del_timer(&priv->return_busy_sense_timer); } init_dev_event = kzalloc(sizeof(struct change_event), GFP_ATOMIC); if (!init_dev_event) break; memcpy(&init_dev_event->event_data, en_reply->EventData, sizeof(Mpi2EventDataSasInitDevStatusChange_t)); init_dev_event->ioc = ioc; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) INIT_WORK(&init_dev_event->work, _initiator_missing_event); #else INIT_WORK(&init_dev_event->work, _initiator_missing_event, (void *)init_dev_event); #endif schedule_work(&init_dev_event->work); break; } case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW: printk(MPT3SAS_INFO_FMT "MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW\n", ioc->stm_name); break; case MPI2_EVENT_HARD_RESET_RECEIVED: { struct MPT_STM_PRIV *priv = ioc->priv; struct DISK *p_disk; u64 lun; printk(MPT3SAS_INFO_FMT "MPI2_EVENT_HARD_RESET_RECEIVED\n", ioc->stm_name); /* fix oops: If this event arrives during 1st port * enable, (which is prior to stm_adapter_install), * the priv object is unitialized. */ if (priv == NULL) break; for (lun = 0; lun < num_luns; lun++) { p_disk = priv->luns[lun]; if (!p_disk) continue; p_disk->reservations_handle = STM_NO_RESERVATIONS; p_disk->ua_asc = 0x29; p_disk->ua_ascq = 0x00; } break; } default: break; } break; } case MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST: { Mpi2TargetCmdBufferPostBaseListReply_t *tcpbl_reply = (Mpi2TargetCmdBufferPostBaseListReply_t *)mpi_reply; u16 io_index = le16_to_cpu(tcpbl_reply->IoIndex); u16 ioc_status = le16_to_cpu(tcpbl_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; struct MPT_STM_PRIV *priv = ioc->priv; if (ioc_status == MPI2_IOCSTATUS_TARGET_ABORTED) _stm_set_io_state(&priv->cmd_buffer[io_index], IO_STATE_POSTED); printk(MPT3SAS_INFO_FMT "CMD_BUF_LIST: io_index(%d), " "flags(0x%02x) ioc_status(0x%04x), loginfo(0x%08x)\n", ioc->stm_name, io_index, tcpbl_reply->Flags, ioc_status, le32_to_cpu(tcpbl_reply->IOCLogInfo)); break; } default: printk(MPT3SAS_INFO_FMT "%s: function(0x%02x)\n", ioc->stm_name, __func__, mpi_reply->Function); break; } return 1; } /** * _stm_target_mode_abort_request_message - generic target mode request abort * @ioc: per adapter object * @io_index: index mapped into command buffer pool * @mid_to_abort: system request message index * @abort_type: ALL_CMD_BUFFERS, ALL_IO, EXACT_IO, EXACT_IO_REQUEST, * IO_REQUEST_AND_IO */ static int _stm_target_mode_abort_request_message(struct MPT3SAS_ADAPTER *ioc, u16 io_index, u32 mid_to_abort, u8 abort_type) { Mpi2TargetModeAbort_t *req; #if defined(TM_ERROR_HANDLING_DEBUG) Mpi2TargetModeAbortReply_t *reply; #endif u32 ioc_state; int rc = 0; u8 issue_reset = 0; u16 smid = USHORT_MAX; mutex_lock(&ioc->stm_tm_cmds.mutex); #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "%s: abort_type(%d), io_index(0x%04x), " "smid(%d)\n", ioc->stm_name, __func__, abort_type, io_index, mid_to_abort); #endif ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { printk(MPT3SAS_ERR_FMT "%s: ioc not operational\n", ioc->stm_name, __func__); rc = -EFAULT; goto out; } if (ioc->stm_tm_cmds.status != MPT3_CMD_NOT_USED) { printk(MPT3SAS_ERR_FMT "%s: config_cmd in use\n", ioc->stm_name, __func__); rc = -EAGAIN; goto out; } smid = mpt3sas_base_get_smid(ioc, ioc->stm_tm_cb_idx); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->stm_name, __func__); rc = -EAGAIN; goto out; } ioc->stm_tm_cmds.status = MPT3_CMD_PENDING; ioc->stm_tm_cmds.smid = smid; req = mpt3sas_base_get_msg_frame(ioc, smid); memset(req, 0, sizeof(Mpi2TargetModeAbort_t)); req->Function = MPI2_FUNCTION_TARGET_MODE_ABORT; req->AbortType = abort_type; req->IoIndexToAbort = cpu_to_le16(io_index); req->MidToAbort = cpu_to_le32(mid_to_abort); init_completion(&ioc->stm_tm_cmds.done); ioc->put_smid_default(ioc, smid); #if defined(STM_RING_BUFFER) stm_rb_abort_request(ioc->priv, io_index, mid_to_abort, smid, abort_type); #endif wait_for_completion_timeout(&ioc->stm_tm_cmds.done, STM_TM_TIMEOUT); if (!(ioc->stm_tm_cmds.status & MPT3_CMD_COMPLETE)) { printk(MPT3SAS_ERR_FMT "%s: timeout\n", ioc->stm_name, __func__); if (!(ioc->stm_tm_cmds.status & MPT3_CMD_RESET)) issue_reset = 1; goto out; } #if defined(TM_ERROR_HANDLING_DEBUG) if (ioc->stm_tm_cmds.status & MPT3_CMD_REPLY_VALID) { reply = (Mpi2TargetModeAbortReply_t *)ioc->stm_tm_cmds.reply; /* normall we drop here */ printk(MPT3SAS_INFO_FMT "%s: io_index(0x%04x), smid(%d)," " ioc_status(0x%04x), loginfo(0x%08x), abort_count(%d)\n", ioc->stm_name, __func__, io_index, mid_to_abort, le16_to_cpu(reply->IOCStatus), le32_to_cpu(reply->IOCLogInfo), le16_to_cpu(reply->AbortCount)); } else { /* TODO anything we need to handle this case ?? */ printk(MPT3SAS_INFO_FMT "%s: NO_REPLY: io_index(0x%04x), " "smid(%d)\n", ioc->stm_name, __func__, io_index, mid_to_abort); } #endif out: if (issue_reset) { mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); rc = -EFAULT; } if (ioc->stm_tm_cmds.smid != USHORT_MAX && ioc->stm_tm_cmds.smid == smid) { //mpt3sas_stm_free_smid(ioc,smid); mpt3sas_base_free_smid(ioc,smid); ioc->stm_tm_cmds.smid = USHORT_MAX; } ioc->stm_tm_cmds.status = MPT3_CMD_NOT_USED; mutex_unlock(&ioc->stm_tm_cmds.mutex); return rc; } /** * _stm_target_mode_abort_imm_done - target assist completion (immediate) * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function. */ static u8 _stm_target_mode_abort_imm_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { struct MPT_STM_PRIV *priv = ioc->priv; Mpi2TargetModeAbort_t *req; Mpi2TargetModeAbortReply_t *mpi_reply; u16 io_index; struct CMD *io_cmd; req = mpt3sas_base_get_msg_frame(ioc, smid); io_index = le16_to_cpu(req->IoIndexToAbort); io_cmd = &priv->cmd_buffer[io_index]; _stm_clear_io_state(io_cmd, IO_STATE_ABORTED); _stm_clear_io_state(io_cmd, IO_STATE_ABORT_IMM); mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) return 1; #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "%s: io_index(0x%04x), smid(%d), " "ioc_status(0x%04x), loginfo(0x%08x), abort_count(%d)\n", ioc->stm_name, __func__, io_index, cpu_to_le16(req->MidToAbort), le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo), le16_to_cpu(mpi_reply->AbortCount)); #endif return 1; } /** * _stm_target_mode_abort_imm - send immediate target assist abort * @io_cmd: per command private data * @mid_to_abort: system request message index * @tag: io tag * * This is called from ISR time, we don't wait for the completion */ static int _stm_target_mode_abort_imm(struct CMD *io_cmd, u32 mid_to_abort, u16 tag) { struct MPT3SAS_ADAPTER *ioc = io_cmd->ioc; Mpi2TargetModeAbort_t *req; u16 smid; #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "%s: io_index(0x%04x), smid(%d)%s, " "tag(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index, mid_to_abort, (mid_to_abort == USHORT_MAX) ? "invalid" : "", tag); #endif if (mid_to_abort == USHORT_MAX) return -1; smid = mpt3sas_base_get_smid(ioc, ioc->stm_tm_imm_cb_idx); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->stm_name, __func__); return -EAGAIN; } req = mpt3sas_base_get_msg_frame(ioc, smid); memset(req, 0, sizeof(Mpi2TargetModeAbort_t)); req->Function = MPI2_FUNCTION_TARGET_MODE_ABORT; req->AbortType = MPI2_TARGET_MODE_ABORT_IO_REQUEST_AND_IO; req->IoIndexToAbort = cpu_to_le16(io_cmd->io_index); req->MidToAbort = cpu_to_le32(mid_to_abort); _stm_set_io_state(io_cmd, IO_STATE_ABORT_IMM); ioc->put_smid_default(ioc, smid); return 0; } /** * _stm_target_mode_abort_exact_io - abort IO with matching io_index * @ioc: per adapter object * @io_cmd: per command private data * * This aborts the io_index controll back to firmware */ static int _stm_target_mode_abort_exact_io(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { return _stm_target_mode_abort_request_message(ioc, io_cmd->io_index, 0, MPI2_TARGET_MODE_ABORT_EXACT_IO); } /** * _stm_target_mode_abort_io_request_and_io - abort IO with matching io_index and smid * @ioc: per adapter object * @io_cmd: per command private data * @smid: system request message index * * This aborts the request associated to the smid. * This aborts the io_index controll back to firmware */ static int _stm_target_mode_abort_io_request_and_io(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd, u16 smid) { if (smid == USHORT_MAX) { printk(MPT3SAS_INFO_FMT "%s: mid_to_abort invalid: " "io_index(0x%04x), tag(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index, io_cmd->tag); return -1; } return _stm_target_mode_abort_request_message(ioc, io_cmd->io_index, smid, MPI2_TARGET_MODE_ABORT_IO_REQUEST_AND_IO); } #if 0 /** * _stm_target_mode_abort_exact_io_request - abort IO with matching io_index and smid * @ioc: per adapter object * @io_cmd: per command private data * @smid: system request message index * * This aborts the request associated to the smid. */ static int _stm_target_mode_abort_exact_io_request(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd, u16 smid) { return _stm_target_mode_abort_request_message(ioc, io_cmd->io_index, smid, MPI2_TARGET_MODE_ABORT_EXACT_IO_REQUEST); } /** * __stm_target_mode_abort_all_io - abort all IO * @ioc: per adapter object * @VP_ID: virtual port id * @VF_ID: virtual function id */ static int __stm_target_mode_abort_all_io(struct MPT3SAS_ADAPTER *ioc, u8 VP_ID, u8 VF_ID) { return _stm_target_mode_abort_request_message(ioc, 0, 0, MPI2_TARGET_MODE_ABORT_ALL_IO); } #endif /** * _stm_target_mode_abort_all - abort all posted command buffers * @ioc: per adapter object * @priv: target private data * @VF_ID: virtual function id */ static int _stm_target_mode_abort_all(struct MPT3SAS_ADAPTER *ioc, u8 VP_ID, u8 VF_ID) { return _stm_target_mode_abort_request_message(ioc, 0, 0, MPI2_TARGET_MODE_ABORT_ALL_CMD_BUFFERS); } /** * _stm_target_mode_abort - tear down command buffers * @ioc: per adapter object * @priv: target private data */ static int _stm_target_mode_abort(struct MPT3SAS_ADAPTER *ioc, struct MPT_STM_PRIV *priv) { struct CMD *io_cmd; int i; /* complete all sleeping threads */ io_cmd = priv->cmd_buffer; for (i = 0; i < priv->num_cmd_buffers; i++, io_cmd) { if ((_stm_check_status(io_cmd, MPT3_CMD_PENDING)) && (io_cmd->tag != INVALID_TAG)) { //mpt3sas_stm_free_smid(ioc, io_cmd->smid); mpt3sas_base_free_smid(ioc, io_cmd->smid); complete(&io_cmd->done); } } _stm_target_mode_abort_all(ioc, 0, 0); return 0; } /** * _initiator_missing_event - sends OP_REMOVE when intiator is removed * @work: struct change_event */ static void #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) _initiator_missing_event(struct work_struct *work) { struct change_event *init_dev_event = container_of(work, struct change_event, work); #else _initiator_missing_event(void *arg) { struct change_event *init_dev_event = (struct change_event *)arg; #endif Mpi2SasIoUnitControlReply_t mpi_reply; Mpi2SasIoUnitControlRequest_t mpi_request; struct MPT3SAS_ADAPTER *ioc = init_dev_event->ioc; struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; u16 initiator_handle; int i, pending_io = 0; unsigned long flags; Mpi2EventDataSasInitDevStatusChange_t *event_data = (Mpi2EventDataSasInitDevStatusChange_t *) &init_dev_event->event_data; initiator_handle = le16_to_cpu(event_data->DevHandle); printk(MPT3SAS_INFO_FMT "init_device_status_change: %s: port(%d), " "handle(0x%04x), sas_address(0x%016llx)\n", ioc->stm_name, (event_data->ReasonCode == MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING) ? "not_responding" : "responding", event_data->PhysicalPort, initiator_handle, (unsigned long long)le64_to_cpu(event_data->SASAddress)); if (event_data->ReasonCode != MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING) goto out; /* abort pending request */ spin_lock_irqsave(&priv->cmd_buffer_lock, flags); io_cmd = priv->cmd_buffer; for (i = 0; i < priv->num_cmd_buffers; io_cmd++, i++) { if (io_cmd->initiator_handle != initiator_handle) continue; if ((_stm_check_io_state(io_cmd, IO_STATE_POSTED) == 1) || io_cmd->tag == INVALID_TAG) continue; _stm_set_io_state(io_cmd, IO_STATE_ABORTED); printk(MPT3SAS_INFO_FMT "[%d]: set aborted flag\n", ioc->stm_name, io_cmd->io_index); if (_stm_check_io_state(io_cmd, IO_STATE_TARGET_ASSIST_STATUS_PEND | IO_STATE_TARGET_ASSIST_DATA_PEND) == 1) { _stm_target_mode_abort_imm(io_cmd, io_cmd->smid, io_cmd->tag); printk(MPT3SAS_INFO_FMT "[%d]: imm: smid(%d), " "tag(%d)\n", ioc->stm_name, io_cmd->io_index, io_cmd->smid, io_cmd->tag); } pending_io = 1; } spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); /* waiting for pending requests to complete */ if (pending_io) ssleep(STM_TARGET_ASSIST_TIMEOUT/HZ); printk(MPT3SAS_INFO_FMT "sending sas_iounit: (OP_REMOVE_DEVICE)" "handle(0x%04x)\n", ioc->stm_name, initiator_handle); memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; mpi_request.DevHandle = cpu_to_le16(initiator_handle); mpi_request.VF_ID = 0; /* TODO */ mpi_request.VP_ID = 0; if ((mpt3sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request)) != 0) printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", init_dev_event->ioc->name, __FILE__, __LINE__, __func__); printk(MPT3SAS_INFO_FMT "sas_iounit: ioc_status" "(0x%04x), loginfo(0x%08x)\n", ioc->stm_name, le16_to_cpu(mpi_reply.IOCStatus), le32_to_cpu(mpi_reply.IOCLogInfo)); out: kfree(init_dev_event); } /** * _stm_cmd_buf_post_list - repost a single command buffer * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_cmd_buf_post_list(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { Mpi2TargetCmdBufferPostListRequest_t *req; #if defined(TM_ERROR_HANDLING_DEBUG) Mpi2TargetCmdBufferPostBaseListReply_t *reply; #endif u16 smid = USHORT_MAX; u32 ioc_state; int rc = 0; u8 issue_reset = 0; mutex_lock(&ioc->stm_tm_cmds.mutex); #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "%s: enter: io_index(0x%04x), tag(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index, io_cmd->tag); #endif ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { printk(MPT3SAS_ERR_FMT "%s: ioc not operational\n", ioc->stm_name, __func__); rc = -EFAULT; goto out; } if (ioc->stm_tm_cmds.status != MPT3_CMD_NOT_USED) { printk(MPT3SAS_ERR_FMT "%s: config_cmd in use\n", ioc->stm_name, __func__); rc = -EAGAIN; goto out; } smid = mpt3sas_base_get_smid(ioc, ioc->stm_tm_cb_idx); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->stm_name, __func__); rc = -EAGAIN; goto out; } ioc->stm_tm_cmds.status = MPT3_CMD_PENDING; ioc->stm_tm_cmds.smid = smid; req = mpt3sas_base_get_msg_frame(ioc, smid); memset(req, 0, sizeof(Mpi2TargetCmdBufferPostListRequest_t)); req->Function = MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST; req->CmdBufferCount = cpu_to_le16(1); req->IoIndex[0] = cpu_to_le16(io_cmd->io_index); init_completion(&ioc->stm_tm_cmds.done); ioc->put_smid_default(ioc, smid); #if defined(STM_RING_BUFFER) stm_rb_post_single(ioc->priv, io_cmd->io_index, io_cmd->tag, smid); #endif wait_for_completion_timeout(&ioc->stm_tm_cmds.done, STM_CMP_POST_TIMEOUT); if (!(ioc->stm_tm_cmds.status & MPT3_CMD_COMPLETE)) { printk(MPT3SAS_ERR_FMT "%s: timeout\n", ioc->stm_name, __func__); if (!(ioc->stm_tm_cmds.status & MPT3_CMD_RESET)) issue_reset = 1; goto out; } #if defined(TM_ERROR_HANDLING_DEBUG) if (ioc->stm_tm_cmds.status & MPT3_CMD_REPLY_VALID) { reply = (Mpi2TargetCmdBufferPostBaseListReply_t *) ioc->stm_tm_cmds.reply; /* normally we drop here */ printk(MPT3SAS_INFO_FMT "%s: exit: io_index(0x%04x), " "ioc_status(0x%04x), loginfo(0x%08x)\n", ioc->stm_name, __func__, io_cmd->io_index, le16_to_cpu(reply->IOCStatus), le32_to_cpu(reply->IOCLogInfo)); } else { /* TODO anything we need to handle this case ?? */ printk(MPT3SAS_INFO_FMT "%s: NO_REPLY: io_index(0x%04x), " "smid(%d)\n", ioc->stm_name, __func__, io_cmd->io_index, io_cmd->smid); } #endif out: if (issue_reset) { mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); rc = -EFAULT; } if (ioc->stm_tm_cmds.smid != USHORT_MAX && ioc->stm_tm_cmds.smid == smid) { //mpt3sas_stm_free_smid(ioc,smid); mpt3sas_base_free_smid(ioc,smid); ioc->stm_tm_cmds.smid = USHORT_MAX; } ioc->stm_tm_cmds.status = MPT3_CMD_NOT_USED; mutex_unlock(&ioc->stm_tm_cmds.mutex); return rc; } /** * _stm_task_mgmt_query - task queury * @priv: target private data * @initiator_handle: initiator device handle * @lun: lun number * @tag: io tag */ static int _stm_task_mgmt_query(struct MPT_STM_PRIV *priv, u16 initiator_handle, u64 lun, u16 tag) { int i; struct CMD *io_cmd; for (i = 0; i < priv->num_cmd_buffers; i++) { io_cmd = &priv->cmd_buffer[i]; if (_stm_check_io_state(io_cmd, IO_STATE_POSTED) == 1) continue; if (initiator_handle == io_cmd->initiator_handle && lun == io_cmd->lun && tag == io_cmd->tag) return MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED; } return MPI2_SCSITASKMGMT_RSP_TM_COMPLETE; } /** * _stm_get_chain_buffer_tracker - obtain chain tracker * @ioc: per adapter object * @smid: smid associated to an IO request * * Returns chain tracker(from ioc->free_chain_list) */ static struct chain_tracker * _stm_get_chain_buffer_tracker(struct MPT3SAS_ADAPTER *ioc, u16 smid) { struct chain_tracker *chain_req; unsigned long flags; /*spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); if (list_empty(&ioc->free_chain_list)) { spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); printk(MPT3SAS_WARN_FMT "chain buffers not available\n", ioc->name); return NULL; } chain_req = list_entry(ioc->free_chain_list.next, struct chain_tracker, tracker_list); list_del_init(&chain_req->tracker_list); list_add_tail(&chain_req->tracker_list, &ioc->scsi_lookup[smid - 1].chain_list); spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);*/ u8 chain_offset = atomic_read(&ioc->chain_lookup[smid - 1].chain_offset); //printk("stm smid(0x%02X) chain_lookup[0x%02X] chain_offset(0x%X)\n", smid, smid-1, chain_offset); if (chain_offset == ioc->chains_needed_per_io) return NULL; chain_req = &ioc->chain_lookup[smid - 1].chains_per_smid[chain_offset]; atomic_inc(&ioc->chain_lookup[smid - 1].chain_offset); return chain_req; } /** * _stm_add_sg_single_ieee - add sg element for IEEE format * @paddr: virtual address for SGE * @flags: SGE flags * @chain_offset: number of 128 byte elements from start of segment * @length: data transfer length * @dma_addr: Physical address * * Return nothing. */ static void _stm_add_sg_single_ieee(void *paddr, u8 flags, u8 chain_offset, u32 length, dma_addr_t dma_addr) { Mpi25IeeeSgeChain64_t *sgel = paddr; sgel->Flags = flags; sgel->NextChainOffset = chain_offset; sgel->Length = cpu_to_le32(length); sgel->Address = cpu_to_le64(dma_addr); } /** * _stm_build_ieee_sgl - building the IEEE version of the SGL * @ioc: per adapter object * @sgl: sgl contents * @smid: system request message index * Context: none. * * The main routine that builds scatter gather table from a given * target assist request. * * Returns 0 success, anything else error */ static int _stm_build_ieee_sgl(struct MPT3SAS_ADAPTER *ioc, struct MPT_SGL *sgl, u16 smid) { Mpi25TargetAssistRequest_t *mpi_request; dma_addr_t chain_dma; void *sg_local, *chain = NULL; u32 chain_offset; u32 chain_length; u32 chain_flags; u32 sges_left; u32 sges_in_segment; u8 simple_sgl_flags; u8 simple_sgl_flags_last; u8 chain_sgl_flags; struct chain_tracker *chain_req; struct MPT_SGE *sgel; mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); /* init scatter gather flags */ simple_sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; simple_sgl_flags_last = simple_sgl_flags | MPI25_IEEE_SGE_FLAGS_END_OF_LIST; chain_sgl_flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT | MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; sges_left = sgl->num_sges; sgel = sgl->sge; sg_local = &mpi_request->SGL; sges_in_segment = (ioc->request_sz - offsetof(Mpi25TargetAssistRequest_t, SGL))/ioc->sge_size_ieee; if (sges_left <= sges_in_segment) goto fill_in_last_segment; mpi_request->ChainOffset = (sges_in_segment - 1 /* chain element */) + (offsetof(Mpi25TargetAssistRequest_t, SGL)/ioc->sge_size_ieee); /* fill in main message segment when there is a chain following */ while (sges_in_segment > 1) { _stm_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, sgel->length, sgel->address); sg_local += ioc->sge_size_ieee; sges_left--; sges_in_segment--; sgel++; } /* initializing the chain flags and pointers */ chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT; chain_req = _stm_get_chain_buffer_tracker(ioc, smid); if (!chain_req) return -1; chain = chain_req->chain_buffer; chain_dma = chain_req->chain_buffer_dma; do { sges_in_segment = (sges_left <= ioc->max_sges_in_chain_message) ? sges_left : ioc->max_sges_in_chain_message; chain_offset = (sges_left == sges_in_segment) ? 0 : sges_in_segment; chain_length = sges_in_segment * ioc->sge_size_ieee; if (chain_offset) chain_length += ioc->sge_size_ieee; _stm_add_sg_single_ieee(sg_local, chain_sgl_flags, chain_offset, chain_length, chain_dma); sg_local = chain; if (!chain_offset) goto fill_in_last_segment; /* fill in chain segments */ while (sges_in_segment) { _stm_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, sgel->length, sgel->address); sg_local += ioc->sge_size_ieee; sges_left--; sges_in_segment--; sgel++; } chain_req = _stm_get_chain_buffer_tracker(ioc, smid); if (!chain_req) return -1; chain = chain_req->chain_buffer; chain_dma = chain_req->chain_buffer_dma; } while (1); fill_in_last_segment: /* fill the last segment */ while (sges_left) { if (sges_left == 1) _stm_add_sg_single_ieee(sg_local, simple_sgl_flags_last, 0, sgel->length, sgel->address); else _stm_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, sgel->length, sgel->address); sg_local += ioc->sge_size_ieee; sges_left--; sgel++; } return 0; } /** * _stm_build_scatter_gather - a sg creation routine for target assist * @ioc: per adapter object * @sgl: sgl contents * @smid: system request message index * Context: none. * * The main routine that builds scatter gather table from a given * target assist request. * * Returns 0 success, anything else error */ static int _stm_build_scatter_gather(struct MPT3SAS_ADAPTER *ioc, struct MPT_SGL *sgl, u16 smid) { return _stm_build_ieee_sgl(ioc, sgl, smid); } /** * _stm_set_sense_info - wrapper for setting sense * @rsp: response data * @sense_key: sense key * @asc: additional sense code * @ascq: additional sense code qualifier * */ static void _stm_set_sense_info(struct ssp_rsp_iu *rsp, int sense_key, int asc, int ascq) { u8 *info; memset(rsp, 0, sizeof(struct ssp_rsp_iu)); rsp->Status = SAM_STAT_CHECK_CONDITION; rsp->DataPres = SSP_SENSE_LEN_VALID; rsp->SenseDataLength = cpu_to_be32(14); info = rsp->ResponseSenseData; info[0] = 0x70; info[2] = (u8)sense_key; info[7] = 6; info[12] = (u8)asc; info[13] = (u8)ascq; } #if 0 /** * _stm_set_status - set the device (SAM) status codes * @rsp: response data * */ static void _stm_set_status(struct ssp_rsp_iu *rsp, int status) { memset(rsp, 0, sizeof(struct ssp_rsp_iu)); rsp->Status = (u8)status; } #endif /** * _stm_reservation_conflict - return reservation conflict * @rsp: response data * */ static void _stm_reservation_conflict(struct ssp_rsp_iu *rsp) { memset(rsp, 0, sizeof(struct ssp_rsp_iu)); rsp->Status = SAM_STAT_RESERVATION_CONFLICT; } #if 0 /** * _stm_set_extra_info - wrapper for setting extra info * @rsp: response data * @valid: * @bits: * @resid: * */ static void _stm_set_extra_info(Mpi2TargetSspRspIu_t *rsp, int valid, int bits, int resid) { u8 *info; info = rsp->ResponseSenseData; info[0] |= (u8)(valid << 7); info[2] |= (u8)(bits << 5); info[3] = (u8)(resid >> 24); info[4] = (u8)(resid >> 16); info[5] = (u8)(resid >> 8); info[6] = (u8)resid; } #endif /** * _stm_set_response_info - wrapper for setting response code * @rsp: response data * @rsp_code: response code * */ static void _stm_set_response_info(struct ssp_rsp_iu *rsp, int rsp_code) { memset(rsp, 0, sizeof(struct ssp_rsp_iu)); rsp->DataPres = SSP_RSP_LEN_VALID; rsp->ResponseDataLength = cpu_to_be32(4); rsp->ResponseSenseData[3] = rsp_code; } /** * _stm_send_target_status - target assist with status (no data) * @ioc: per adapter object * @io_cmd: per command buffer object * @status_flags: target assist flags */ static int _stm_send_target_status(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd, u8 status_flags) { struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; Mpi2TargetStatusSendRequest_t *req; struct ssp_rsp_iu *rsp; Mpi2TargetErrorReply_t *reply; u32 ioc_state; u16 ioc_status; u16 smid = 0; int rc = 0; u32 sgl_flags; int line_no = 0; unsigned long flags; u32 sgl_data_sz; u8 ieee_sgl_flags; ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { printk(MPT3SAS_ERR_FMT "%s: ioc not operational, io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -EFAULT; goto out; } if (_stm_check_io_state(io_cmd, IO_STATE_RESET) == 1) { printk(MPT3SAS_ERR_FMT "%s: host reset, io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = EFAULT; goto out; } if (!_stm_check_status(io_cmd, MPT3_CMD_NOT_USED)) { printk(MPT3SAS_ERR_FMT "%s: command buffer in use, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -EAGAIN; goto out; } if (_stm_check_io_state(io_cmd, IO_STATE_POSTED) == 1) { printk(MPT3SAS_ERR_FMT "%s: cmd already posted!!!, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -ENXIO; goto out; } if (_stm_set_status_pending(io_cmd) != 0) goto abort_io; /* if IO_STATE_ABORTED */ //smid = mpt3sas_stm_get_smid_scsiio(ioc, ioc->stm_io_cb_idx, NULL); smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->stm_io_cb_idx, NULL); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -EAGAIN; goto out; } dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s:enter, io_index(0x%04x), " "smid(%d)\n", ioc->stm_name, __func__, io_cmd->io_index, smid)); io_cmd->smid = smid; req = mpt3sas_base_get_msg_frame(ioc, smid); memset(req, 0, sizeof(Mpi2TargetStatusSendRequest_t)); req->Function = MPI2_FUNCTION_TARGET_STATUS_SEND; req->StatusFlags = status_flags; req->QueueTag = cpu_to_le16(be16_to_cpu(tscb_reply->Tag)); req->IoIndex = cpu_to_le16(io_cmd->io_index); req->InitiatorConnectionTag = cpu_to_le16( be16_to_cpu(tscb_reply->InitiatorConnectionTag)); if (!(status_flags & MPI2_TSS_FLAGS_AUTO_GOOD_STATUS)) { req->SGLOffset0 = offsetof(Mpi2TargetStatusSendRequest_t, StatusDataSGE) / 4; sgl_data_sz = offsetof(Mpi2TargetSspRspIu_t, ResponseSenseData); rsp = io_cmd->response.va; if (rsp->DataPres & SSP_SENSE_LEN_VALID) sgl_data_sz += be32_to_cpu(rsp->SenseDataLength); else if (rsp->DataPres & SSP_RSP_LEN_VALID) sgl_data_sz += be32_to_cpu(rsp->ResponseDataLength); //if (ioc->mpi25) { ieee_sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | MPI25_IEEE_SGE_FLAGS_END_OF_LIST | MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; _stm_add_sg_single_ieee(&req->StatusDataSGE, ieee_sgl_flags, 0, sgl_data_sz, io_cmd->response.pa); /*} else { req->SGLFlags = cpu_to_le16(MPI2_TSS_SGLFLAGS_MPI_TYPE + MPI2_TSS_SGLFLAGS_SYSTEM_ADDR); sgl_flags = (MPI2_SGE_FLAGS_HOST_TO_IOC | MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT; ioc->base_add_sg_single(&req->StatusDataSGE, sgl_flags | sgl_data_sz, io_cmd->response.pa); }*/ } init_completion(&io_cmd->done); spin_lock_irqsave(&io_cmd->io_state_lock, flags); if (io_cmd->io_state & IO_STATE_ABORTED) { spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); io_cmd->smid = USHORT_MAX; //mpt3sas_stm_free_smid(ioc, smid); mpt3sas_base_free_smid(ioc, smid); goto abort_io; } io_cmd->io_state |= IO_STATE_TARGET_ASSIST_STATUS_PEND; ioc->put_smid_target_assist(ioc, smid, io_cmd->io_index); spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); #if defined(STM_RING_BUFFER) stm_rb_ta_status(ioc->priv, io_cmd->io_index, io_cmd->tag, smid); #endif wait_for_completion_timeout(&io_cmd->done, STM_TARGET_ASSIST_STATUS_TIMEOUT); _stm_clear_io_state(io_cmd, IO_STATE_TARGET_ASSIST_STATUS_PEND); if (!_stm_check_status(io_cmd, MPT3_CMD_COMPLETE)) { if (!_stm_check_io_state(io_cmd, IO_STATE_RESET)) { printk(MPT3SAS_ERR_FMT "%s: TIMEOUT, io_index(0x%04x)," " smid(%d), tag(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index, smid, io_cmd->tag); if (io_cmd->smid == smid) _stm_target_mode_abort_io_request_and_io(ioc, io_cmd, smid); else _stm_target_mode_abort_exact_io(ioc, io_cmd); _stm_cmd_buf_post_list(ioc, io_cmd); line_no = __LINE__; } else { printk(MPT3SAS_ERR_FMT "%s: host_reset, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; } if (io_cmd->smid == smid) //mpt3sas_stm_free_smid(ioc, smid); mpt3sas_base_free_smid(ioc, smid); rc = -EFAULT; goto out; } if (_stm_check_status(io_cmd, MPT3_CMD_REPLY_VALID)) { reply = (Mpi2TargetErrorReply_t *)io_cmd->reply; printk(MPT3SAS_INFO_FMT "%s exit: io_index(0x%04x), smid(%d), " "tag(0x%04x), lun(%lld)\n\tioc_status(0x%04x), " "loginfo(0x%08x), transfer_count(0x%08x)\n", ioc->stm_name, __func__, io_cmd->io_index, smid, io_cmd->tag, (unsigned long long)io_cmd->lun, le16_to_cpu(reply->IOCStatus), le32_to_cpu(reply->IOCLogInfo), le32_to_cpu(reply->TransferCount)); ioc_status = le16_to_cpu(reply->IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status == MPI2_IOCSTATUS_TARGET_NO_CONNECTION || ioc_status == MPI2_IOCSTATUS_TARGET_ABORTED) { _stm_cmd_buf_post_list(ioc, io_cmd); } else { _stm_target_mode_abort_exact_io(ioc, io_cmd); _stm_cmd_buf_post_list(ioc, io_cmd); } line_no = __LINE__; rc = -EFAULT; } out: _stm_set_status_not_used(io_cmd); io_cmd->smid = USHORT_MAX; if (!rc) { dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s:exit, " "io_index(0x%04x), smid(%d), \n", ioc->stm_name, __func__, io_cmd->io_index, smid)); } else { printk(MPT3SAS_ERR_FMT "%s:exit, aborting IO, " "io_index(0x%04x), line(%d)!!!\n", ioc->stm_name, __func__, io_cmd->io_index, line_no); } return rc; abort_io: printk(MPT3SAS_ERR_FMT "TAS: aborting cmd: lun(%lld), " "initiator_handle(0x%04x), io_index(0x%04x), " "tag(0x%04x)\n", ioc->stm_name, (unsigned long long)io_cmd->lun, io_cmd->initiator_handle, io_cmd->io_index, be16_to_cpu(tscb_reply->Tag)); _stm_target_mode_abort_exact_io(ioc, io_cmd); _stm_clear_io_state(io_cmd, IO_STATE_ABORTED); io_cmd->tag = INVALID_TAG; _stm_cmd_buf_post_list(ioc, io_cmd); _stm_set_status_not_used(io_cmd); return -EFAULT; } /** * _stm_send_target_data - target assist with data * @ioc: per adapter object * @io_cmd: per command buffer object * @target_assist_flags: flags */ static int _stm_send_target_data(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd, struct DISK *p_disk, u8 target_assist_flags) { struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; Mpi25TargetAssistRequest_t *req; Mpi2TargetErrorReply_t *reply; u32 ioc_state; u16 ioc_status; u16 smid = -1; int rc = 0; int line_no = 0; unsigned long flags; ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { printk(MPT3SAS_ERR_FMT "%s: ioc not operational, io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -EFAULT; goto out; } if (_stm_check_io_state(io_cmd, IO_STATE_RESET) == 1) { printk(MPT3SAS_ERR_FMT "%s: host reset, io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = EFAULT; goto out; } if (!_stm_check_status(io_cmd, MPT3_CMD_NOT_USED)) { printk(MPT3SAS_ERR_FMT "%s: cmd buffers in use, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -EAGAIN; goto out; } if (_stm_check_io_state(io_cmd, IO_STATE_POSTED) == 1) { printk(MPT3SAS_ERR_FMT "%s: cmd already posted!!!, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -ENXIO; goto out; } if (_stm_set_status_pending(io_cmd) != 0) goto abort_io; /* if IO_STATE_ABORTED */ //smid = mpt3sas_stm_get_smid_scsiio(ioc, ioc->stm_io_cb_idx, NULL); smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->stm_io_cb_idx, NULL); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; rc = -EAGAIN; goto out; } dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s:enter, io_index(0x%04x), " "smid(%d)\n", ioc->stm_name, __func__, io_cmd->io_index, smid)); io_cmd->smid = smid; req = mpt3sas_base_get_msg_frame(ioc, smid); memset(req, 0, offsetof(Mpi25TargetAssistRequest_t, SGL)); req->Function = MPI2_FUNCTION_TARGET_ASSIST; req->TargetAssistFlags = target_assist_flags; req->QueueTag = cpu_to_le16(be16_to_cpu(tscb_reply->Tag)); req->IoIndex = cpu_to_le16(io_cmd->io_index); req->InitiatorConnectionTag = cpu_to_le16( be16_to_cpu(tscb_reply->InitiatorConnectionTag)); req->DataLength = cpu_to_le32(io_cmd->sgl.length); req->SGLOffset0 = offsetof(Mpi25TargetAssistRequest_t, SGL) / 4; if (_stm_build_scatter_gather(ioc, &io_cmd->sgl, smid)) { printk(MPT3SAS_ERR_FMT "%s: failure creating sgl!!!, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); //mpt3sas_stm_free_smid(ioc, smid); mpt3sas_base_free_smid(ioc, smid); io_cmd->smid = USHORT_MAX; line_no = __LINE__; rc = -ENXIO; goto out; } init_completion(&io_cmd->done); spin_lock_irqsave(&io_cmd->io_state_lock, flags); if (io_cmd->io_state & IO_STATE_ABORTED) { spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); //mpt3sas_stm_free_smid(ioc, smid); mpt3sas_base_free_smid(ioc, smid); io_cmd->smid = USHORT_MAX; goto abort_io; } io_cmd->io_state |= IO_STATE_TARGET_ASSIST_DATA_PEND; ioc->put_smid_target_assist(ioc, smid, io_cmd->io_index); spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); #if defined(STM_RING_BUFFER) stm_rb_ta_data(ioc->priv, io_cmd->io_index, io_cmd->tag, smid); #endif wait_for_completion(&io_cmd->done); _stm_clear_io_state(io_cmd, IO_STATE_TARGET_ASSIST_DATA_PEND); if (!_stm_check_status(io_cmd, MPT3_CMD_COMPLETE)) { if (!_stm_check_io_state(io_cmd, IO_STATE_RESET)) { printk(MPT3SAS_ERR_FMT "%s: TIMEOUT, io_index(0x%04x)," " smid(%d), tag(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index, smid, io_cmd->tag); if (io_cmd->smid == smid) _stm_target_mode_abort_io_request_and_io(ioc, io_cmd, smid); else _stm_target_mode_abort_exact_io(ioc, io_cmd); _stm_cmd_buf_post_list(ioc, io_cmd); line_no = __LINE__; } else { printk(MPT3SAS_ERR_FMT "%s: host_reset, " "io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); line_no = __LINE__; } if (io_cmd->smid == smid) //mpt3sas_stm_free_smid(ioc, smid); mpt3sas_base_free_smid(ioc, smid); rc = -EFAULT; goto out; } if (_stm_check_status(io_cmd, MPT3_CMD_REPLY_VALID)) { reply = (Mpi2TargetErrorReply_t *)io_cmd->reply; printk(MPT3SAS_INFO_FMT "%s exit: io_index(0x%04x), smid(%d), " "tag(0x%04x), lun(%lld)\n\tioc_status(0x%04x), " "loginfo(0x%08x), transfer_count(0x%08x)\n", ioc->stm_name, __func__, io_cmd->io_index, smid, io_cmd->tag, (unsigned long long)io_cmd->lun, le16_to_cpu(reply->IOCStatus), le32_to_cpu(reply->IOCLogInfo), le32_to_cpu(reply->TransferCount)); ioc_status = le16_to_cpu(reply->IOCStatus) & MPI2_IOCSTATUS_MASK; _stm_set_status_not_used(io_cmd); switch (ioc_status) { case MPI2_IOCSTATUS_TARGET_ABORTED: case MPI2_IOCSTATUS_TARGET_NO_CONNECTION: _stm_cmd_buf_post_list(ioc, io_cmd); break; case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE: _stm_set_sense_info( (struct ssp_rsp_iu *)io_cmd->response.va, ABORTED_COMMAND, 0x4B, 0x07); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); break; case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR: _stm_set_sense_info( (struct ssp_rsp_iu *)io_cmd->response.va, ABORTED_COMMAND, 0x4B, 0x05); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); break; case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA: _stm_set_sense_info( (struct ssp_rsp_iu *)io_cmd->response.va, ABORTED_COMMAND, 0x4B, 0x02); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); break; case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT: _stm_set_sense_info( (struct ssp_rsp_iu *)io_cmd->response.va, ABORTED_COMMAND, 0x4B, 0x03); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); break; case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED: _stm_set_sense_info( (struct ssp_rsp_iu *)io_cmd->response.va, ABORTED_COMMAND, 0x4B, 0x04); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); break; default: case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH: case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT: _stm_set_sense_info( (struct ssp_rsp_iu *)io_cmd->response.va, ABORTED_COMMAND, 0x0, 0x00); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); break; } line_no = __LINE__; rc = -EFAULT; } out: _stm_set_status_not_used(io_cmd); io_cmd->smid = USHORT_MAX; if (!rc) { dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s:exit, " "io_index(0x%04x), smid(%d)\n", ioc->stm_name, __func__, io_cmd->io_index, smid)); } else { printk(MPT3SAS_ERR_FMT "%s:exit, aborting IO, " "io_index(0x%04x), line(%d)!!!\n", ioc->stm_name, __func__, io_cmd->io_index, line_no); } return rc; abort_io: printk(MPT3SAS_ERR_FMT "TAD: aborting cmd: lun(%lld), " "initiator_handle(0x%04x), io_index(0x%04x), tag(0x%04x)\n", ioc->stm_name, (unsigned long long)io_cmd->lun, io_cmd->initiator_handle, io_cmd->io_index, be16_to_cpu(tscb_reply->Tag)); _stm_target_mode_abort_exact_io(ioc, io_cmd); io_cmd->tag = INVALID_TAG; _stm_clear_io_state(io_cmd, IO_STATE_ABORTED); _stm_cmd_buf_post_list(ioc, io_cmd); _stm_set_status_not_used(io_cmd); return -EFAULT; } /** * _stm_inquiry - process an INQUIRY request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_inquiry(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; struct DISK *p_disk; u8 *data; u32 length; int page; u64 lun; int rc = 0; struct MPT_SGL *sgl; lun = getlun(tscb_reply->LogicalUnitNumber, 0); p_disk = priv->luns[lun]; data = (u8 *)p_disk->data.va; length = get2bytes(tscb_reply->CDB, 3); if (lun < num_luns) data[0] = 0x00; else data[0] = 0x7f; if (ignore_lun_zero == 1 && lun == 0) data[0] = 0x20; if (tscb_reply->CDB[1] & (1<<0)) { page = tscb_reply->CDB[2]; dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "page(%x), length(%d)\n", ioc->stm_name, page, length)); switch (page) { case (0x00): data[1] = 0x00; data[2] = 0x00; data[3] = 7 - 4; data[4] = 0x00; data[5] = 0x80; data[6] = 0x83; if (length > 7) length = 7; break; case (0x80): data[1] = 0x80; data[2] = 0x00; data[3] = 28 - 4; sprintf(data + 4, "0x%016llX-%04X", (unsigned long long)STM_SAS_ADDR, (u32)lun); if (length > 28) length = 28; break; case (0x83): data[1] = 0x83; data[2] = 0x00; data[3] = 28 - 4; data[4] = 0x01; data[5] = 0x03; data[6] = 0x00; data[7] = 8; ((__be64 *)(data + 8))[0] = cpu_to_be64(STM_SAS_ADDR); data[16] = 0x01; data[17] = 0x00; data[18] = 0x00; data[19] = 4; data[20] = 0x00; putlun(data, 21, lun); data[23] = 0x00; data[24] = 0x00; data[25] = 0x00; data[26] = 0x00; data[27] = 0x00; if (length > 28) length = 28; break; default: rc = -1; break; } } else { data[1] = 0x00; data[2] = 0x05; data[3] = 0x32; data[4] = 58 - 5; data[5] = 0x00; data[6] = 0x00; data[7] = 0x32; memcpy(data + 8, "Linux ", 8); if (mpt_stm_hostname[0] == '\0') memcpy(data + 16, "AvagoTech.Virtual", 16); else memcpy(data + 16, mpt_stm_hostname, 16); sprintf(data + 32, "%02x%02x", ioc->pdev->bus->number, ioc->pdev->devfn); ((__be64 *)(data + 36))[0] = cpu_to_be64(STM_SAS_ADDR); data[44] = 0x00; data[45] = (u8)lun; memset(data + 46, 0, 58 - 46); data[56] = 0x0f; if (length > 58) length = 58; } sgl = &io_cmd->sgl; sgl->num_sges = 1; sgl->data_direction = DMA_TO_DEVICE; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; return rc; } /** * _stm_read_capacity - process a READ_CAPACITY request * @ioc: per adapter object * @io_cmd: per command buffer object */ static void _stm_read_capacity(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; __be32 *data; struct MPT_SGL *sgl; u64 block_count; struct DISK *p_disk; p_disk = priv->luns[io_cmd->lun]; data = (__be32 *)p_disk->data.va; block_count = p_disk->blk_cnt; data[0] = (block_count >> 32) ? cpu_to_be32(0xffffffff) : cpu_to_be32(block_count - 1); data[1] = cpu_to_be32(p_disk->block_size); sgl = &io_cmd->sgl; sgl->num_sges = 1; sgl->data_direction = DMA_TO_DEVICE; sgl->length = 8; sgl->sge[0].length = 8; sgl->sge[0].address = p_disk->data.pa; } /** * _stm_service_action_in - process a SERVICE_ACTION_IN request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_service_action_in(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; __be32 *data; struct MPT_SGL *sgl; u32 length; u64 block_count; int rc = 0; struct DISK *p_disk; p_disk = priv->luns[io_cmd->lun]; data = (__be32 *)p_disk->data.va; if ((tscb_reply->CDB[1] & 0x1f) == 0x10) { length = tscb_reply->CDB[13]; memset(data, 0, length); block_count = p_disk->blk_cnt; data[0] = cpu_to_be32((block_count - 1) >> 32); data[1] = cpu_to_be32(block_count - 1); data[2] = cpu_to_be32(p_disk->block_size); data[12] = 0x00; sgl = &io_cmd->sgl; sgl->num_sges = 1; sgl->data_direction = DMA_TO_DEVICE; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; rc = 0; } else rc = -1; return rc; } /** * _stm_request_sense - process a REQUEST_SENSE request * @ioc: per adapter object * @io_cmd: per command buffer object */ static void _stm_request_sense(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; u32 *data; struct MPT_SGL *sgl; u8 length; struct DISK *p_disk; p_disk = priv->luns[io_cmd->lun]; data = (u32 *)p_disk->data.va; length = (tscb_reply->CDB[4] > 14) ? 14 : tscb_reply->CDB[4]; memset(data, 0, length); data[0] = 0x70; data[7] = 6; sgl = &io_cmd->sgl; sgl->num_sges = 1; sgl->data_direction = DMA_TO_DEVICE; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; } /** * _stm_report_luns - process a REPORT_LUNS request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_report_luns(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; u32 length; u32 out_length = 0; int i; int start_lun; __be32 *data; struct MPT_SGL *sgl; struct DISK *p_disk; p_disk = priv->luns[io_cmd->lun]; data = (__be32 *)p_disk->data.va; out_length = num_luns * 8 + 8; if (ignore_lun_zero == 1) { out_length -= 8; start_lun = 1; } else start_lun = 0; /* NOTE: * We can handle up to 511 luns. * Beyond that, we will need to implement an larger buffer. */ if (out_length > ECHO_BYTES) return -1; *data++ = cpu_to_be32(out_length - 8); *data++ = 0; for (i = start_lun; i < num_luns; i++) { *data++ = cpu_to_be32((0x3ff & i) << 16 | ((i > 0xff) ? (0x1 << 30) : 0)); *data++ = 0; } length = be32_to_cpu(*(__be32 *)&tscb_reply->CDB[6]); length = min_t(u32, length, out_length); sgl = &io_cmd->sgl; sgl->num_sges = 1; sgl->data_direction = DMA_TO_DEVICE; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; return 0; } /** * _stm_fill_sgl - generic function for filling in sgl for data transfer * @ioc: per adapter object * @io_cmd: per command buffer object * @dir: data direction * @lbn: logical block number * @lbn_count: number of block in this xfer */ static int _stm_fill_sgl(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd, enum dma_data_direction dir, u64 lbn, u32 lbn_count) { struct MPT_STM_PRIV *priv = ioc->priv; struct dma_access *slice; struct MPT_SGL *sgl; struct DISK *p_disk; u64 lbn_offset; u64 bytes_avail; u64 total_length, xfer_length; u64 current_lbn, last_lbn; u64 lun; u64 slice_index; u64 completed_lbn; lun = io_cmd->lun; p_disk = priv->luns[lun]; /* beyond end of media sanity check */ if (lbn >= p_disk->blk_cnt) { printk(MPT3SAS_INFO_FMT "%s: fail, request beyond end media\n", ioc->stm_name, __func__); return -1; } /* xfer size sanity check */ if ((lbn_count * p_disk->block_size) > (p_disk->bytes_per_slice * MPT3STM_SG_DEPTH)) { printk(MPT3SAS_INFO_FMT "%s: fail, xfer size too big\n", ioc->stm_name, __func__); return -1; } sgl = &io_cmd->sgl; sgl->length = 0; sgl->num_sges = 0; sgl->data_direction = dir; total_length = lbn_count * p_disk->block_size; current_lbn = lbn; slice_index = lbn; do_div(slice_index, p_disk->blocks_per_slice); slice = &p_disk->slice[slice_index]; lbn_offset = lbn - (slice_index * BLOCKS_PER_SLICE); last_lbn = lbn + lbn_count; #if defined(TM_VERBOSE_SG_DEBUGING) printk(MPT3SAS_INFO_FMT "lun(%lld), lbn(%lld), lbn_count(%d), " "length(%lld), slice_index(%lld), lbn_offset(%lld)\n", ioc->stm_name, lun, lbn, lbn_count, total_length, slice_index, lbn_offset); #endif while (total_length && current_lbn < last_lbn) { bytes_avail = (BLOCKS_PER_SLICE - lbn_offset) * p_disk->block_size; xfer_length = min_t(u64, total_length, bytes_avail); if (!slice->va) { /* allocation of ramdisk on the fly */ slice->va = dma_alloc_coherent(&ioc->pdev->dev, p_disk->bytes_per_slice, &slice->pa, GFP_KERNEL); if (!slice->va) { printk(MPT3SAS_WARN_FMT "lun(%lld), lbn(%lld): " "no more available memory for " "ramdisk!!!\n", ioc->stm_name, (unsigned long long)lun, (unsigned long long)lbn); return -1; } memset(slice->va, 0, p_disk->bytes_per_slice); } sgl->sge[sgl->num_sges].address = slice->pa; if (lbn_offset) { sgl->sge[sgl->num_sges].address += (lbn_offset * p_disk->block_size); lbn_offset = 0; } sgl->sge[sgl->num_sges].length = xfer_length; #if defined(TM_VERBOSE_SG_DEBUGING) printk(MPT3SAS_INFO_FMT "[%d]: address(0x%08llx), length(%d), " "bytes_avail(%lld)\n", ioc->stm_name, sgl->num_sges, sgl->sge[sgl->num_sges].address, sgl->sge[sgl->num_sges].length, bytes_avail); #endif sgl->length += xfer_length; sgl->num_sges++; if (sgl->num_sges > MPT3STM_SG_DEPTH) { printk("sgl->num_sges(%d), MPT3STM_SG_DEPTH(%d)\n", sgl->num_sges, MPT3STM_SG_DEPTH); BUG(); /* TODO - return data underrun */ } total_length -= xfer_length; slice++; completed_lbn = xfer_length; do_div(completed_lbn, p_disk->block_size); current_lbn += completed_lbn; } #if defined(TM_VERBOSE_SG_DEBUGING) printk(MPT3SAS_INFO_FMT "lun(%lld), sg_elem(%d), sz(%d)\n\n", ioc->stm_name, lun, sgl->num_sges, sgl->length); #endif return 0; } #define STM_READ_WRITE_RETURN_DATA (0) /* CQ 229358 - for zero length R/W's the driver shouldn't do target assist */ #define STM_READ_WRITE_RETURN_GOOD_STATUS (1) /* if the LBA is beyond end disk or xfer size > 512KB, return ILLEGAL_REQUEST */ #define STM_READ_WRITE_RETURN_ILLEGAL_REQUEST (2) /** * _stm_read_write_disk - process a data transfer request * @ioc: per adapter object * @io_cmd: per command buffer object * @dir: data direction * * returns STM_READ_WRITE_RETURN_XXXX code defined above */ static int _stm_read_write_disk(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd, enum dma_data_direction dir) { struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; u64 lbn; u32 lbns; struct DISK *p_disk; p_disk = priv->luns[io_cmd->lun]; switch (tscb_reply->CDB[0]) { case READ_6: case WRITE_6: lbn = get3bytes(tscb_reply->CDB, 1) & 0x1fffff; lbns = tscb_reply->CDB[4] == 0 ? 256 : tscb_reply->CDB[4]; break; case READ_10: case WRITE_10: case WRITE_VERIFY: lbn = get4bytes(tscb_reply->CDB, 2); lbns = get2bytes(tscb_reply->CDB, 7); break; case READ_12: case WRITE_12: case WRITE_VERIFY_12: lbn = get4bytes(tscb_reply->CDB, 2); lbns = get4bytes(tscb_reply->CDB, 6); break; case READ_16: case WRITE_16: case 0x8E: /* WRITE_VERIFY_16 */ lbn = get8bytes(tscb_reply->CDB, 2); lbns = get4bytes(tscb_reply->CDB, 10); break; case READ_LONG: case WRITE_LONG: lbn = get4bytes(tscb_reply->CDB, 2); lbns = get2bytes(tscb_reply->CDB, 7); if (lbns != 0 && lbns != p_disk->block_size) return STM_READ_WRITE_RETURN_ILLEGAL_REQUEST; break; default: return STM_READ_WRITE_RETURN_ILLEGAL_REQUEST; } if (!lbns) return STM_READ_WRITE_RETURN_GOOD_STATUS; if (_stm_fill_sgl(ioc, io_cmd, dir, lbn, lbns) == 0) return STM_READ_WRITE_RETURN_DATA; else return STM_READ_WRITE_RETURN_ILLEGAL_REQUEST; } /** * _stm_verify_disk - process a VERIFY request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_verify_disk(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; u64 lbn; u32 lbns; switch (tscb_reply->CDB[0]) { case VERIFY: lbn = get4bytes(tscb_reply->CDB, 2); lbns = get2bytes(tscb_reply->CDB, 7); break; case 0xAF: /* VERIFY_12 */ lbn = get4bytes(tscb_reply->CDB, 2); lbns = get4bytes(tscb_reply->CDB, 6); break; case VERIFY_16: lbn = get8bytes(tscb_reply->CDB, 2); lbns = get4bytes(tscb_reply->CDB, 10); break; default: return -1; } /* printk(MPT3SAS_INFO_FMT "%s: lbn(%lld), lbn_count(%d), length(%d)\n", * ioc->stm_name, __func__, lbn, lbns, lbns * p_disk->block_size); */ if (!(tscb_reply->CDB[1] & 0x2)) /* BYTCHK = 1, incoming data */ { /* TODO validate data is there */ return 0; } else { return -1; } } /** * _stm_write_buffer - process a WRITE_BUFFER request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_write_buffer(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; u32 length; struct MPT_SGL *sgl; struct DISK *p_disk; if (tscb_reply->CDB[2] != 0 || tscb_reply->CDB[3] != 0 || tscb_reply->CDB[4] != 0 || tscb_reply->CDB[5] != 0) return -1; p_disk = priv->luns[io_cmd->lun]; length = get3bytes(tscb_reply->CDB, 6); switch (tscb_reply->CDB[1] & 0x0f) { case 0x2: case 0xa: if (length > ECHO_BYTES) length = ECHO_BYTES; break; default: return -1; break; } sgl = &io_cmd->sgl; sgl->num_sges = 1; sgl->data_direction = DMA_TO_DEVICE; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; return 0; } /** * _stm_read_buffer - process a READ_BUFFER request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_read_buffer(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; u32 length; struct MPT_SGL *sgl; u8 *data; struct DISK *p_disk; p_disk = priv->luns[io_cmd->lun]; data = p_disk->data.va; if (tscb_reply->CDB[2] != 0 || tscb_reply->CDB[3] != 0 || tscb_reply->CDB[4] != 0 || tscb_reply->CDB[5] != 0) return -1; length = get3bytes(tscb_reply->CDB, 6); switch (tscb_reply->CDB[1] & 0x0f) { case 0x2: case 0xa: if (length > ECHO_BYTES) length = ECHO_BYTES; break; case 0x3: case 0xb: if (length > 4) length = 4; ((__be32 *)data)[0] = cpu_to_be32(ECHO_BYTES); data[0] = 0xff; break; default: return -1; break; } sgl = &io_cmd->sgl; sgl->num_sges = 1; sgl->data_direction = DMA_FROM_DEVICE; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; return 0; } /** * _stm_mode_sense - process a MODE_SENSE request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_mode_sense(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; struct MPT_STM_PRIV *priv = ioc->priv; struct MPT_SGL *sgl; struct DISK *p_disk = NULL; u8 *data; u64 block_count; u32 length; u32 out_length = 0; u32 block_size; u8 page_code; u8 pc; u8 subpage_control; p_disk = priv->luns[io_cmd->lun]; data = p_disk->data.va; if (tscb_reply->CDB[0] == MODE_SENSE) length = tscb_reply->CDB[4]; else if (tscb_reply->CDB[0] == MODE_SENSE_10) length = get2bytes(tscb_reply->CDB, 7); else return -1; page_code = tscb_reply->CDB[2] & 0x3f; pc = (tscb_reply->CDB[2] & 0xc0) >> 6; /* page control bit */ subpage_control = tscb_reply->CDB[3]; dTMprintk(ioc, printk(MPT3SAS_INFO_FMT"%s enter: page_code(0x%02x), " "pc(0x%02x), subpage_control(0x%02x)\n", ioc->stm_name, __func__, page_code, pc, subpage_control)); if (subpage_control) /* only support page zero */ return -1; block_count = p_disk->blk_cnt; block_size = p_disk->block_size; memset(data, 0, length); if (tscb_reply->CDB[1] & 0x08) { /* disable block descriptor */ if (tscb_reply->CDB[0] == MODE_SENSE_10) { data += 4; out_length += 4; data[2] = 0; /* Block Descriptor Length */ } data[3] = 0; /* Block Descriptor Length */ data += 4; out_length += 4; } else { if (tscb_reply->CDB[0] == MODE_SENSE_10) { data += 4; out_length += 4; } data[3] = 8; /* Block Descriptor Length */ *(__be32 *)(data + 4) = cpu_to_be32(block_count); *(__be32 *)(data + 8) = cpu_to_be32(block_size); data += 12; out_length += 12; } /* Read-Write error recovery page */ if (page_code == 0x01 || page_code == 0x3f) { data[0] = 0x01; /* Mode Data Length */ data[1] = 10; /* Medium Type */ if (pc != 1) data[2] = 0xe4; /* Device-Specific Parameter */ data += 12; out_length += 12; } /* Format (obsolete) page */ if (page_code == 0x03 || page_code == 0x3f) { u16 num_sect = 128; data[0] = 0x03; data[1] = 22; if (pc != 1) { *(__be16 *)(data + 10) = cpu_to_be16(num_sect); *(__be16 *)(data + 12) = cpu_to_be16(block_size); data[20] = 0x40; } data += 24; out_length += 24; } /* Rigid disk geometry (obsolete) page */ if (page_code == 0x04 || page_code == 0x3f) { u32 num_cyl, num_head = 16, num_sect = 128; u32 num_per_cyl = num_head * num_sect; num_cyl = block_count + num_per_cyl - 1; data[0] = 0x04; data[1] = 22; if (pc != 1) { data[2] = (u8)(num_cyl >> 16); data[3] = (u8)(num_cyl >> 8); data[4] = (u8)num_cyl; data[5] = (u8)num_head; *(__be16 *)(data + 20) = cpu_to_be16(3600); } data += 24; out_length += 24; } /* Caching page */ if (page_code == 0x08 || page_code == 0x3f) { data[0] = 0x08; data[1] = 18; switch (pc) { case 1: data[2] = 0x05; break; case 2: data[2] = 0x00; break; default: data[2] = p_disk->cache_control; break; } data += 20; out_length += 20; } /* Informational exceptions control page */ if (page_code == 0x1c || page_code == 0x3f) { data[0] = 0x1c; data[1] = 10; switch (pc) { case 1: data[2] = 0xbb; data[3] = 0x0f; break; case 2: data[2] = 0x00; data[3] = 0x00; break; default: data[2] = p_disk->exception_control; data[3] = p_disk->exception_method; break; } data += 12; out_length += 12; } /* TODO: Protocal-Specific Port Mode Page 0x19 */ if (out_length <= 12) return -1; data = p_disk->data.va; /* Mode Data Length - overwrites everything done in if-stmts */ data[0] = out_length - 1; length = min_t(u32, length, out_length); sgl = &io_cmd->sgl; sgl->data_direction = DMA_TO_DEVICE; sgl->num_sges = 1; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; return 0; } /** * _stm_mode_select - process a MODE_SELECT request * @ioc: per adapter object * @io_cmd: per command buffer object */ static int _stm_mode_select(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; struct MPT_STM_PRIV *priv = ioc->priv; struct MPT_SGL *sgl; struct DISK *p_disk = NULL; u8 *data; u8 page_code; u32 length; short i; if (tscb_reply->CDB[0] == MODE_SELECT) length = tscb_reply->CDB[4]; else if (tscb_reply->CDB[0] == MODE_SELECT_10) length = get2bytes(tscb_reply->CDB, 7); else return -1; p_disk = priv->luns[io_cmd->lun]; data = p_disk->data.va; /* ask for the data */ sgl = &io_cmd->sgl; sgl->data_direction = DMA_FROM_DEVICE; sgl->num_sges = 1; sgl->length = length; sgl->sge[0].length = length; sgl->sge[0].address = p_disk->data.pa; if (_stm_send_target_data(ioc, io_cmd, p_disk, 0) != 0) return -1; dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s enter: length(%d)\n", ioc->stm_name, __func__, length)); if (length >= 12) { if (tscb_reply->CDB[0] == MODE_SELECT_10) data += 4; if (data[3] == 8) { i = get4bytes(data, 8); dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: block_size(%d)\n", ioc->stm_name, __func__, i)); if (i == BLOCK_BYTES_MIN || i == BLOCK_BYTES_MAX) p_disk->new_block_size = i; else goto bad_parameter; } } else if (tscb_reply->CDB[0] == MODE_SELECT_10) data += 4; data += data[3] + 4; while (data < (u8 *)p_disk->data.va + length) { page_code = data[0] & 0x3f; dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: page_code(0x%02x)\n", ioc->stm_name, __func__, page_code)); switch (page_code) { case 0x08: if (data[1] != 18) goto bad_parameter; if (data[2] & 0xfa) goto bad_parameter; p_disk->cache_control = data[2]; data += 20; break; case 0x1c: if (data[1] != 10) goto bad_parameter; if (data[2] & 0x44) goto bad_parameter; if (data[3] & 0xf0) goto bad_parameter; p_disk->exception_control = data[2]; p_disk->exception_method = data[3]; data += 12; break; default: goto bad_parameter; break; } } /* send status */ _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_AUTO_GOOD_STATUS | MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); return 0; bad_parameter: _stm_set_sense_info((struct ssp_rsp_iu *)io_cmd->response.va, ILLEGAL_REQUEST, 0x26, 0x02); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); return -1; } /** * _stm_format_unit - FORMAT_UNIT implementation * @ioc: per adapter object * @io_cmd: per command buffer object * Context: user * * Number of blocks to clear is specified in STM_WIPE_BLOCKS */ static void _stm_format_unit(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct MPT_STM_PRIV *priv = ioc->priv; u64 lun = io_cmd->lun; struct DISK *p_disk; int i; struct dma_access *slice; p_disk = priv->luns[lun]; /* wipe out the ramdisk */ for (i = 0, slice = p_disk->slice; i < p_disk->max_slices; i++, slice++) { if (!slice->va) continue; memset(slice->va, 0, p_disk->bytes_per_slice); } } static int _stm_is_initialized(struct DISK *p_disk) { struct dma_access *slice; int i; if (!p_disk) return 0; if (p_disk->is_initialized) return 1; if (!p_disk->slice) return 0; for (i = 0, slice = p_disk->slice; i < p_disk->max_slices; i++, slice++) { if (!slice->va) return 0; } p_disk->is_initialized = 1; return 1; } static int _stm_is_read_write_cmd(int command) { if (command == READ_6 || command == WRITE_6 || command == READ_10 || command == WRITE_10 || command == READ_12 || command == READ_16 || command == WRITE_16 || command == READ_LONG || command == WRITE_LONG || command == WRITE_VERIFY || command == WRITE_VERIFY_12 || command == 0x8E) return 1; else return 0; } static void _stm_ssp_mutex_lock(struct DISK *p_disk, int command, u16 io_index) { /* Don't do mutexs for READS/WRITES/VERIFY once the LUN has been * initialized */ if (!p_disk) return; if (_stm_is_read_write_cmd(command)) if (_stm_is_initialized(p_disk)) return; mutex_lock(&p_disk->data_mutex); p_disk->io_index_locked_for_xfer = io_index; } static void _stm_ssp_mutex_unlock(struct DISK *p_disk, u16 io_index) { if (!p_disk) return; if (p_disk->io_index_locked_for_xfer == io_index) { p_disk->io_index_locked_for_xfer = USHORT_MAX; mutex_unlock(&p_disk->data_mutex); } } /** * _stm_process_ssp_cmd_frame - main handler for SSP COMMAND IU * @ioc: per adapter object * @io_cmd: per command buffer object * Context: user * */ static void _stm_process_ssp_cmd_frame(struct CMD *io_cmd) { struct MPT3SAS_ADAPTER *ioc = io_cmd->ioc; struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_cmd_buffer *tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; struct ssp_rsp_iu *response_iu = (struct ssp_rsp_iu *)io_cmd->response.va; int command, rc; u64 lun; u16 tag; u8 target_assist_flags; u8 send_data; struct DISK *p_disk = NULL; if (priv->stopping_threads) return; lun = io_cmd->lun; tag = be16_to_cpu(tscb_reply->Tag); command = tscb_reply->CDB[0]; target_assist_flags = MPI2_TSS_FLAGS_REPOST_CMD_BUFFER; send_data = 0; if (_stm_check_io_state(io_cmd, IO_STATE_POSTED) == 1) { printk(MPT3SAS_ERR_FMT "cmd already posted: lun(%lld), " "initiator_handle(0x%04x), io_index(0x%04x), " "tag(0x%04x)\n", ioc->stm_name, (unsigned long long)lun, io_cmd->initiator_handle, io_cmd->io_index, tag); return; } if (lun >= num_luns) { printk(MPT3SAS_ERR_FMT "%s: lun(%lld) too large!!!\n", ioc->stm_name, __func__, (unsigned long long)lun); _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x20, 0x00); goto out; } #if 0 printk(MPT3SAS_INFO_FMT "SSP_CMD_IU: enter: " "scsi_cmd(0x%02x), lun(%lld), initiator_handle(0x%04x), " "io_index(0x%04x), tag(0x%04x)\n", ioc->stm_name, command, lun, io_cmd->initiator_handle, io_cmd->io_index, tag); #endif p_disk = priv->luns[lun]; _stm_ssp_mutex_lock(p_disk, command, io_cmd->io_index); memset(response_iu, 0, sizeof(struct ssp_rsp_iu)); if (ignore_lun_zero == 1 && lun == 0 && command != INQUIRY && command != REPORT_LUNS) { _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x25, 0x00); goto out; } /* If there's a pending Unit Attention, deliver it */ if (p_disk->ua_asc || p_disk->ua_ascq) { printk(MPT3SAS_ERR_FMT "%s: ua!!!\n", ioc->stm_name, __func__); _stm_set_sense_info(response_iu, UNIT_ATTENTION, p_disk->ua_asc, p_disk->ua_ascq); p_disk->ua_asc = 0; p_disk->ua_ascq = 0; goto out; } /* check for reservations */ if (p_disk->reservations_handle != STM_NO_RESERVATIONS && p_disk->reservations_handle != io_cmd->initiator_handle) { printk(MPT3SAS_ERR_FMT "%s: reservations!!!\n", ioc->stm_name, __func__); _stm_reservation_conflict(response_iu); goto out; } dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "SSP_CMD_IU: enter: " "scsi_cmd(0x%02x), lun(%lld), initiator_handle(0x%04x), " "io_index(0x%04x), tag(0x%04x)\n", ioc->stm_name, command, (unsigned long long)lun, io_cmd->initiator_handle, io_cmd->io_index, tag)); switch (command) { case INQUIRY: target_assist_flags = MPI2_TSS_FLAGS_REPOST_CMD_BUFFER; if (!(_stm_inquiry(ioc, io_cmd))) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; } else _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case START_STOP: if (likely(!priv->return_busy_sense)) { printk(MPT3SAS_INFO_FMT "START_STOP (NOT_BUSY)\n", ioc->stm_name); target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; } else { printk(MPT3SAS_INFO_FMT "START_STOP (BUSY)\n", ioc->stm_name); _stm_set_sense_info(response_iu, NOT_READY, 0x04, 0x01); } break; case READ_CAPACITY: _stm_read_capacity(ioc, io_cmd); target_assist_flags = MPI2_TSS_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; break; case SERVICE_ACTION_IN_16: if (!(_stm_service_action_in(ioc, io_cmd))) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; } else _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case REQUEST_SENSE: _stm_request_sense(ioc, io_cmd); target_assist_flags = MPI2_TSS_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; break; case READ_6: case READ_10: case READ_12: case READ_16: rc = _stm_read_write_disk(ioc, io_cmd, DMA_TO_DEVICE); if (rc == STM_READ_WRITE_RETURN_DATA) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; } else if (rc == STM_READ_WRITE_RETURN_ILLEGAL_REQUEST) { _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x21, 0x00); } else if (rc == STM_READ_WRITE_RETURN_GOOD_STATUS) { target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; } break; case READ_LONG: rc = _stm_read_write_disk(ioc, io_cmd, DMA_TO_DEVICE); if (rc == STM_READ_WRITE_RETURN_DATA) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; } else if (rc == STM_READ_WRITE_RETURN_ILLEGAL_REQUEST) { _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); } else if (rc == STM_READ_WRITE_RETURN_GOOD_STATUS) { target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; } break; case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: case WRITE_VERIFY: case WRITE_VERIFY_12: case 0x8E: /* WRITE_VERIFY_16 */ rc = _stm_read_write_disk(ioc, io_cmd, DMA_FROM_DEVICE); if (rc == STM_READ_WRITE_RETURN_DATA) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS; send_data = 1; } else if (rc == STM_READ_WRITE_RETURN_ILLEGAL_REQUEST) { _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x21, 0x00); } else if (rc == STM_READ_WRITE_RETURN_GOOD_STATUS) { target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; } break; case WRITE_LONG: rc = _stm_read_write_disk(ioc, io_cmd, DMA_FROM_DEVICE); if (rc == STM_READ_WRITE_RETURN_DATA) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS; send_data = 1; } else if (rc == STM_READ_WRITE_RETURN_ILLEGAL_REQUEST) { _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); } else if (rc == STM_READ_WRITE_RETURN_GOOD_STATUS) { target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; } break; case WRITE_SAME: case 0x93: /* WRITE_SAME_16 */ _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case VERIFY: case 0xAF: /* VERIFY_12 */ case VERIFY_16: if (!(_stm_verify_disk(ioc, io_cmd))) target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; else _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case SYNCHRONIZE_CACHE: target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; break; case TEST_UNIT_READY: if (likely(!priv->return_busy_sense)) { //printk(MPT3SAS_DEBUG_FMT "TEST_UNIT_READY (NOT_BUSY)\n", // ioc->stm_name); target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; } else { printk(MPT3SAS_INFO_FMT "TEST_UNIT_READY (BUSY)\n", ioc->stm_name); _stm_set_sense_info(response_iu, NOT_READY, 0x04, 0x01); } break; case LOG_SENSE: _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case MODE_SENSE: case MODE_SENSE_10: if (!(_stm_mode_sense(ioc, io_cmd))) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; } else _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case MODE_SELECT: case MODE_SELECT_10: _stm_mode_select(ioc, io_cmd); return; break; case REPORT_LUNS: if (!(_stm_report_luns(ioc, io_cmd))) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; } else _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case PERSISTENT_RESERVE_IN: case PERSISTENT_RESERVE_OUT: /* TODO these should be implemented since we have support for * RESERVE/RELEASE */ _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case RESERVE: p_disk->reservations_handle = io_cmd->initiator_handle; target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; break; case RELEASE: p_disk->reservations_handle = STM_NO_RESERVATIONS; target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; break; case READ_BUFFER: if (!(_stm_read_buffer(ioc, io_cmd))) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS | MPI2_TARGET_ASSIST_FLAGS_DATA_DIRECTION; send_data = 1; } else _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case WRITE_BUFFER: if (!(_stm_write_buffer(ioc, io_cmd))) { target_assist_flags = MPI2_TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER | MPI2_TARGET_ASSIST_FLAGS_AUTO_STATUS; send_data = 1; } else _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; case FORMAT_UNIT: _stm_format_unit(ioc, io_cmd); target_assist_flags |= MPI2_TSS_FLAGS_AUTO_GOOD_STATUS; break; default: _stm_set_sense_info(response_iu, ILLEGAL_REQUEST, 0x24, 0x00); break; } out: if (send_data) _stm_send_target_data(ioc, io_cmd, p_disk, target_assist_flags); else _stm_send_target_status(ioc, io_cmd, target_assist_flags); /* unlock data xfer mutex */ _stm_ssp_mutex_unlock(p_disk, io_cmd->io_index); dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "SSP_CMD_IU: exit: " "io_index(0x%04x), tag(0x%04x)\n", ioc->stm_name, io_cmd->io_index, tag)); } /** * _stm_task_mgmt_abort - aborting any pending target assist * @ioc: per adapter object * @tm_tag: SSP_TASK_FRAME tag */ static void _stm_task_mgmt_abort(struct MPT3SAS_ADAPTER *ioc, u16 tm_tag) { struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; int i, counter; u16 smid; #if defined(TM_ERROR_HANDLING_DEBUG) printk(KERN_INFO "%s: enter\n", __func__); #endif io_cmd = priv->cmd_buffer; for (i = 0; i < priv->num_cmd_buffers; io_cmd++, i++) { if ((_stm_check_io_state(io_cmd, IO_STATE_ABORTED) == 0) || io_cmd->tm_tag != tm_tag) continue; if (_stm_check_io_state(io_cmd, IO_STATE_ABORT_IMM) == 1) { #if defined(TM_ERROR_HANDLING_DEBUG) printk(KERN_INFO "%s: IMM: io_index(0x%04x), " "tag(0x%04x)\n", __func__, io_cmd->io_index, io_cmd->tag); #endif /* yield to the other thread to complete */ counter = 0; while ((io_cmd->io_state & IO_STATE_ABORTED) && counter++ < 500) msleep(1); #if defined(TM_ERROR_HANDLING_DEBUG) if (io_cmd->io_state & IO_STATE_ABORTED) printk(KERN_INFO "%s: IMM_STILL_ABORTED: " "io_index(0x%04x), tag(0x%04x)\n", __func__, io_cmd->io_index, io_cmd->tag); #endif } else if (_stm_check_io_state(io_cmd, IO_STATE_TARGET_ASSIST_STATUS_PEND | IO_STATE_TARGET_ASSIST_DATA_PEND) == 1) { /* kill target assist */ #if defined(TM_ERROR_HANDLING_DEBUG) printk(KERN_INFO "%s: TA: io_index(0x%04x), " "tag(0x%04x)\n", __func__, io_cmd->io_index, io_cmd->tag); #endif smid = io_cmd->smid; if (io_cmd->smid != USHORT_MAX) { _stm_target_mode_abort_io_request_and_io(ioc, io_cmd, io_cmd->smid); if (io_cmd->smid == smid) { #if defined(TM_ERROR_HANDLING_DEBUG) printk(KERN_INFO "%s: TA_STILL_ACTIVE:" " io_index(0x%04x), tag(0x%04x)\n", __func__, io_cmd->io_index, io_cmd->tag); #endif io_cmd->tag = INVALID_TAG; io_cmd->smid = USHORT_MAX; //mpt3sas_stm_free_smid(ioc, smid); mpt3sas_base_free_smid(ioc, smid); _stm_set_status_not_used(io_cmd); _stm_cmd_buf_post_list(ioc, io_cmd); } } } else { #if defined(TM_ERROR_HANDLING_DEBUG) printk(KERN_INFO "%s: XIO: io_index(0x%04x), " "tag(0x%04x)\n", __func__, io_cmd->io_index, io_cmd->tag); #endif /* yield to the other thread to complete */ counter = 0; while ((io_cmd->io_state & IO_STATE_ABORTED) && counter++ < 500) msleep(1); #if defined(TM_ERROR_HANDLING_DEBUG) if (io_cmd->io_state & IO_STATE_ABORTED) printk(KERN_INFO "%s: XIO_STILL_ABORTED: " "io_index(0x%04x), tag(0x%04x)\n", __func__, io_cmd->io_index, io_cmd->tag); #endif } _stm_clear_io_state(io_cmd, IO_STATE_ABORTED); } #if defined(TM_ERROR_HANDLING_DEBUG) printk(KERN_INFO "%s; exit\n", __func__); #endif } /** * _stm_process_task_cmd_frame - main handler for SSP_TASK_FRAME * @ioc: per adapter object * @io_cmd: per command buffer object * Context: user */ static void _stm_process_task_cmd_frame(struct CMD *io_cmd) { struct MPT3SAS_ADAPTER *ioc = io_cmd->ioc; struct MPT_STM_PRIV *priv = ioc->priv; struct ssp_task_buffer *tstb_reply = (struct ssp_task_buffer *)io_cmd->cmd; struct ssp_rsp_iu *response_iu = (struct ssp_rsp_iu *)io_cmd->response.va; int response; u64 lun; u16 tag, tm_tag; struct CMD *io_cmd_tmp; int i; unsigned long flags; if (priv->stopping_threads) return; tm_tag = be16_to_cpu(tstb_reply->Tag); tag = be16_to_cpu(tstb_reply->ManagedTaskTag); lun = io_cmd->lun; if (lun >= num_luns) response = MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN; else response = MPI2_SCSITASKMGMT_RSP_TM_COMPLETE; #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "SSP_TASK_IU: enter: " "function(0x%02x), io_index(0x%04x), tag(0x%04x), lun(%lld)," " tm_count(%d)\n", ioc->stm_name, tstb_reply->TaskManagementFunction, io_cmd->io_index, tag, (unsigned long long)lun, io_cmd->tm_count); #endif if (tstb_reply->TaskManagementFunction == TM_QUERY_TASK) { response = _stm_task_mgmt_query(priv, io_cmd->initiator_handle, lun, tag); } else if (tstb_reply->TaskManagementFunction == TM_ABORT_TASK || tstb_reply->TaskManagementFunction == TM_ABORT_TASK_SET || tstb_reply->TaskManagementFunction == TM_CLEAR_TASK_SET || tstb_reply->TaskManagementFunction == TM_LOGICAL_UNIT_RESET || tstb_reply->TaskManagementFunction == TM_TARGET_RESET) { if (io_cmd->tm_count) _stm_task_mgmt_abort(ioc, tm_tag); } else response = MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED; spin_lock_irqsave(&priv->cmd_buffer_lock, flags); for (i = 0, io_cmd_tmp = priv->cmd_buffer; i < priv->num_cmd_buffers; io_cmd_tmp++, i++) { if (io_cmd_tmp->tm_tag == tm_tag) io_cmd_tmp->tm_tag = INVALID_TAG; } spin_unlock_irqrestore(&priv->cmd_buffer_lock, flags); _stm_set_response_info(response_iu, response); _stm_send_target_status(ioc, io_cmd, MPI2_TSS_FLAGS_REPOST_CMD_BUFFER); #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "SSP_TASK_IU: exit: " "io_index(0x%04x), tag(0x%04x), lun(%lld)\n", ioc->stm_name, io_cmd->io_index, tag, (unsigned long long)lun); #endif } /** * _stm_task_set_abort_flag - set IO_STATE_ABORTED bits for cmds needing aborting * @ioc: per adapter object * @handle: initiator handle * @lun: lun number * @tag: io tag * @tm_tag: SSP_TASK_FRAME tag * @tm_count: number of cmds needing aborting. */ static void _stm_task_set_abort_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, u16 tag, u16 tm_tag, u16 tm_io_index, u16 *tm_count) { struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; unsigned long flags; int i; io_cmd = priv->cmd_buffer; for (i = 0; i < priv->num_cmd_buffers; io_cmd++, i++) { spin_lock_irqsave(&io_cmd->io_state_lock, flags); if ((io_cmd->io_state & IO_STATE_POSTED) || (io_cmd->io_index == tm_io_index)) { spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); continue; } if ((handle == ALL_INITIATOR || handle == io_cmd->initiator_handle) && (lun == ALL_LUN || lun == io_cmd->lun) && (tag == ALL_TAG || tag == io_cmd->tag)) { io_cmd->io_state |= IO_STATE_ABORTED; io_cmd->tm_tag = tm_tag; *tm_count = *tm_count + 1; if (io_cmd->io_state & (IO_STATE_TARGET_ASSIST_STATUS_PEND | IO_STATE_TARGET_ASSIST_DATA_PEND)) { spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); _stm_target_mode_abort_imm(io_cmd, io_cmd->smid, io_cmd->tag); } else spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); #if defined(TM_ERROR_HANDLING_DEBUG) printk("%s: IO_STATE_ABORTED: smid(%d), " "io_index=(0x%04x), state(0x%04x), status(0x%04x)," " lun(%lld), tag(0x%04x)\n", __func__, io_cmd->smid, io_cmd->io_index, io_cmd->io_state, io_cmd->status, (unsigned long long)io_cmd->lun, io_cmd->tag); #endif } else { spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); #if defined(TM_ERROR_HANDLING_DEBUG) printk("%s: IO_STATE_ABORTED(skip): smid(%d), " "io_index=(0x%04x), state(0x%04x), status(0x%04x)," " lun(%lld), tag(0x%04x)\n", __func__, io_cmd->smid, io_cmd->io_index, io_cmd->io_state, io_cmd->status, (unsigned long long)io_cmd->lun, io_cmd->tag); #endif } } #if defined(TM_ERROR_HANDLING_DEBUG) printk("%s:count(%d)\n", __func__, *tm_count); #endif if (*tm_count == 0) return; } /** * _stm_abort_io - aborting IO * @ioc: per adapter object * @function: task management function * @handle: initiator handle * @lun: lun number * @tag: io tag * @tm_tag: SSP_TASK_FRAME tag * @tm_count: number of cmds needing aborting. */ static void _stm_abort_io(struct MPT3SAS_ADAPTER *ioc, u8 function, u16 handle, u64 lun, u16 tag, u16 tm_tag, u16 tm_io_index, u16 *tm_count) { if (lun >= num_luns) return; switch (function) { case TM_ABORT_TASK: /* terminate the command for the ITLQ nexus */ #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_ABORT_TASK - lun(%lld), " "tag(0x%04x)\n", ioc->stm_name, lun, tag); #endif _stm_task_set_abort_flag(ioc, handle, lun, tag, tm_tag, tm_io_index, tm_count); break; case TM_ABORT_TASK_SET: /* terminate all commands for the ITL nexus */ #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_ABORT_TASK_SET - lun(%lld)\n", ioc->stm_name, lun); #endif _stm_task_set_abort_flag(ioc, handle, lun, ALL_TAG, tm_tag, tm_io_index, tm_count); break; case TM_CLEAR_TASK_SET: /* terminate all commands from all initiators for this target */ #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_CLEAR_TASK_SET - lun(%lld)\n", ioc->stm_name, lun); #endif _stm_task_set_abort_flag(ioc, ALL_INITIATOR, lun, ALL_TAG, tm_tag, tm_io_index, tm_count); break; case TM_LOGICAL_UNIT_RESET: /* perform LU reset on this target and LUN combo */ #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_LOGICAL_UNIT_RESET - lun(%lld)\n", ioc->stm_name, lun); #endif _stm_task_set_abort_flag(ioc, ALL_INITIATOR, lun, ALL_TAG, tm_tag, tm_io_index, tm_count); break; case TM_TARGET_RESET: /* IT nexus */ /* target reset is not defined in SAS spec, but I guess we will * support it */ #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_TARGET_RESET\n", ioc->stm_name); #endif _stm_task_set_abort_flag(ioc, handle, ALL_LUN, ALL_TAG, tm_tag, tm_io_index, tm_count); break; case TM_QUERY_TASK: #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_QUERY_TASK - lun(%lld), " "tag(0x%04x)\n", ioc->stm_name, lun, tag); #endif break; } } void _stm_start_io(struct MPT3SAS_ADAPTER *ioc, struct CMD *io_cmd) { struct ssp_cmd_buffer *tscb_reply; struct ssp_task_buffer *tstb_reply; u16 tag, tm_tag; unsigned long flags; switch (io_cmd->cmd[0]) { case SSP_CMD_FRAME: spin_lock_irqsave(&io_cmd->io_state_lock, flags); tscb_reply = (struct ssp_cmd_buffer *)io_cmd->cmd; io_cmd->tag = be16_to_cpu(tscb_reply->Tag); io_cmd->lun = getlun(tscb_reply->LogicalUnitNumber, 0); io_cmd->io_state &= ~IO_STATE_POSTED; spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: io_index(0x%04x), " "initiator_handle(0x%04x), phy_number(%d), tag(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index, io_cmd->initiator_handle, io_cmd->phy_number, io_cmd->tag)); #if defined(STM_RING_BUFFER) stm_rb_cmd_buffer(priv, io_index, SSP_CMD_FRAME, tscb_reply->CDB[0], io_cmd->tag); #endif break; case SSP_TASK_FRAME: spin_lock_irqsave(&io_cmd->io_state_lock, flags); tstb_reply = (struct ssp_task_buffer *)io_cmd->cmd; io_cmd->lun = getlun(tstb_reply->LogicalUnitNumber, 0); io_cmd->tag = INVALID_TAG; io_cmd->tm_count = 0; tm_tag = be16_to_cpu(tstb_reply->Tag); tag = be16_to_cpu(tstb_reply->ManagedTaskTag); io_cmd->io_state &= ~IO_STATE_POSTED; spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); #if defined(STM_RING_BUFFER) stm_rb_cmd_buffer(priv, io_index, SSP_TASK_FRAME, tstb_reply->TaskManagementFunction, tag); #endif #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_CMD_BUF: io_index(0x%04x), " "initiator_handle(0x%04x), phy_number(%d), tm_tag(0x%04x)," " tag(0x%04x)\n", ioc->stm_name, io_cmd->io_index, io_cmd->initiator_handle, io_cmd->phy_number, tm_tag, tag); #endif _stm_abort_io(ioc, tstb_reply->TaskManagementFunction, io_cmd->initiator_handle, io_cmd->lun, tag, tm_tag, io_cmd->io_index, &io_cmd->tm_count); #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "%s: done, io_index(0x%04x)\n", ioc->stm_name, __func__, io_cmd->io_index); #endif break; } if (io_cmd->cmd_thread) wake_up_process(io_cmd->cmd_thread); } /** * mpt3sas_stmapp_target_command - main entry point to processing command * buffers. * @ioc: per adapter object * @rpf: reply descriptor * Context: interrupt * * The IOC posts a target command buffer reply descriptor after it recieves a * new SCSI command from initiator and transfers it to a command buffer on the * host. The host uses io_index to determine which command buffer was used for * the new SCSI command. */ void mpt3sas_stmapp_target_command(struct MPT3SAS_ADAPTER *ioc, Mpi2TargetCommandBufferReplyDescriptor_t *rpf, u8 msix_index) { struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; u16 io_index = le16_to_cpu(rpf->IoIndex); unsigned long flags; io_cmd = &priv->cmd_buffer[io_index]; io_cmd->initiator_handle = le16_to_cpu(rpf->InitiatorDevHandle); io_cmd->phy_number = rpf->Flags & MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK; io_cmd->msix_index = msix_index; spin_lock_irqsave(&io_cmd->io_state_lock, flags); if ((io_cmd->io_state & IO_STATE_POSTED) != IO_STATE_POSTED) { io_cmd->out_of_order_cmp = 1; spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); return; } spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); _stm_start_io(ioc, io_cmd); } /** * mpt3sas_stm_command_buffer_reply - process target mode reply (success) * @ioc: per adapter object * @rpf: reply descriptor * Context: interrupt * * The IOC uses a target assist success reply descriptor when a target assist or * target status send request completes without any errors. */ void mpt3sas_stm_target_assist_success_reply(struct MPT3SAS_ADAPTER *ioc, Mpi2TargetAssistSuccessReplyDescriptor_t *rpf) { struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; u16 io_index = le16_to_cpu(rpf->IoIndex); u16 smid = le16_to_cpu(rpf->SMID); dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: io_index(0x%04x), " "smid(%d)\n", ioc->stm_name, __func__, io_index, smid)); io_cmd = &priv->cmd_buffer[io_index]; if (_stm_check_status(io_cmd, MPT3_CMD_NOT_USED)) { printk(MPT3SAS_INFO_FMT "%s exit: %d\n", ioc->stm_name, __func__, __LINE__); return; } _stm_set_io_state(io_cmd, IO_STATE_POSTED); io_cmd->tag = INVALID_TAG; io_cmd->smid = USHORT_MAX; _stm_set_status(io_cmd, MPT3_CMD_COMPLETE); _stm_clear_status(io_cmd, MPT3_CMD_PENDING); #if defined(STM_RING_BUFFER) stm_rb_ta_reply_success(priv, io_index, io_cmd->tag, smid); #endif io_cmd->ta_wait_count = 0; if (io_cmd->out_of_order_cmp) { io_cmd->out_of_order_cmp = 0; _stm_start_io(ioc, io_cmd); } complete(&io_cmd->done); } /** * _stm_io_done - process standard target mode reply (error) * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt * * Return 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function. */ static u8 _stm_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { struct MPT_STM_PRIV *priv = ioc->priv; Mpi2TargetErrorReply_t *mpi_reply; u16 io_index; struct CMD *io_cmd; dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: smid(%d)\n", ioc->stm_name, __func__, smid)); mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) { printk(MPT3SAS_INFO_FMT "%s exit: %d\n", ioc->stm_name, __func__, __LINE__); return 1; } io_index = le16_to_cpu(mpi_reply->IoIndex); io_cmd = &priv->cmd_buffer[io_index]; if (_stm_check_status(io_cmd, MPT3_CMD_NOT_USED)) { printk(MPT3SAS_INFO_FMT "%s exit: %d\n", ioc->stm_name, __func__, __LINE__); return 1; } #if defined(STM_RING_BUFFER) stm_rb_ta_reply_error(priv, io_index, io_cmd->tag, smid); #endif #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "TM_ERROR_REPLY: io_index(0x%04x), " "tag(0x%04x), smid(%d), ta_wait_count(%d)\n", ioc->stm_name, io_cmd->io_index, io_cmd->tag, smid, io_cmd->ta_wait_count); #endif memcpy(io_cmd->reply, mpi_reply, mpi_reply->MsgLength*4); _stm_clear_io_state(io_cmd, IO_STATE_ABORTED); _stm_set_status(io_cmd, MPT3_CMD_COMPLETE); _stm_set_status(io_cmd, MPT3_CMD_REPLY_VALID); _stm_clear_status(io_cmd, MPT3_CMD_PENDING); io_cmd->tag = INVALID_TAG; io_cmd->smid = USHORT_MAX; io_cmd->ta_wait_count = 0; complete(&io_cmd->done); return 1; } /** * _stm_tm_done - internal request callback for task managment * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt * * Return 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function. */ static u8 _stm_tm_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { struct MPT_STM_PRIV *priv = ioc->priv; MPI2DefaultReply_t *mpi_reply; Mpi2TargetCmdBufferPostListRequest_t *req; u16 io_index; struct CMD *io_cmd; dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: smid(%d)\n", ioc->stm_name, __func__, smid)); mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) return 1; if (ioc->stm_tm_cmds.status == MPT3_CMD_NOT_USED) return 1; #if defined(STM_RING_BUFFER) stm_rb_abort_reply(ioc->priv, smid); #endif if (mpi_reply->Function == MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST) { req = mpt3sas_base_get_msg_frame(ioc, smid); io_index = le16_to_cpu(req->IoIndex[0]); io_cmd = &priv->cmd_buffer[io_index]; printk(MPT3SAS_INFO_FMT "POST_CMD_BUFFER: io_index(0x%04x)\n", ioc->stm_name, io_index); _stm_set_io_state(io_cmd, IO_STATE_POSTED); if (io_cmd->out_of_order_cmp) { io_cmd->out_of_order_cmp = 0; _stm_start_io(ioc, io_cmd); } } memcpy(ioc->stm_tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ioc->stm_tm_cmds.status |= (MPT3_CMD_REPLY_VALID + MPT3_CMD_COMPLETE); ioc->stm_tm_cmds.status &= ~MPT3_CMD_PENDING; ioc->stm_tm_cmds.smid = USHORT_MAX; complete(&ioc->stm_tm_cmds.done); return 1; } /** * _stm_post_done - internal request callback for posting command buffers * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt * * Return 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function. */ static u8 _stm_post_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; dTMprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: smid(%d)\n", ioc->stm_name, __func__, smid)); mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) return 1; if (ioc->stm_post_cmds.status == MPT3_CMD_NOT_USED) return 1; #if defined(STM_RING_BUFFER) stm_rb_post_all_reply(ioc->priv, smid); #endif memcpy(ioc->stm_post_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ioc->stm_post_cmds.status |= (MPT3_CMD_REPLY_VALID + MPT3_CMD_COMPLETE); ioc->stm_post_cmds.status &= ~MPT3_CMD_PENDING; ioc->stm_post_cmds.smid = USHORT_MAX; complete(&ioc->stm_post_cmds.done); return 1; } /** * _stm_cmd_buf_post_base - reporting command buffers at driver load time and * reset. * @ioc: per adapter object * @priv: target private data */ static int _stm_cmd_buf_post_base(struct MPT3SAS_ADAPTER *ioc, struct MPT_STM_PRIV *priv) { Mpi2TargetCmdBufferPostBaseRequest_t *req; Mpi2TargetCmdBufferPostBaseListReply_t *reply; struct CMD *io_cmd; int i; u16 smid; u32 ioc_state; int rc = 0; u8 issue_reset = 0; ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { printk(MPT3SAS_ERR_FMT "%s: ioc not operational\n", ioc->stm_name, __func__); rc = -EFAULT; return rc; } if (ioc->stm_post_cmds.status != MPT3_CMD_NOT_USED) { printk(MPT3SAS_ERR_FMT "%s: config_cmd in use\n", ioc->stm_name, __func__); rc = -EAGAIN; return rc; } smid = mpt3sas_base_get_smid(ioc, ioc->stm_post_cb_idx); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->stm_name, __func__); rc = -EAGAIN; return rc; } ioc->stm_post_cmds.status = MPT3_CMD_PENDING; ioc->stm_post_cmds.smid = smid; req = mpt3sas_base_get_msg_frame(ioc, smid); memset(req, 0, sizeof(Mpi2TargetCmdBufferPostBaseRequest_t)); req->BufferPostFlags = MPI2_CMD_BUF_POST_BASE_FLAGS_AUTO_POST_ALL; req->Function = MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST; req->TotalCmdBuffers = cpu_to_le16(priv->num_cmd_buffers); req->CmdBufferLength = cpu_to_le16(CMD_IU_SIZE); req->VP_ID = 0; /* TODO */ req->VF_ID = 0; req->BaseAddressLow = cpu_to_le32(priv->cmd.pa); #if BITS_PER_LONG == 64 req->BaseAddressHigh = cpu_to_le32(priv->cmd.pa>>32); #endif printk(MPT3SAS_INFO_FMT "%s: smid(%d), total_cmd_buffers(%d), " "cmd_buffer_length(%d)\n", ioc->stm_name, __func__, smid, priv->num_cmd_buffers, CMD_IU_SIZE); for (i = 0, io_cmd = priv->cmd_buffer; i < priv->num_cmd_buffers; i++, io_cmd++) { io_cmd->status = MPT3_CMD_NOT_USED; io_cmd->tag = INVALID_TAG; io_cmd->tm_tag = INVALID_TAG; io_cmd->smid = USHORT_MAX; _stm_clear_io_state(io_cmd, IO_STATE_RESET); _stm_set_io_state(io_cmd, IO_STATE_POSTED); } init_completion(&ioc->stm_post_cmds.done); ioc->put_smid_default(ioc, smid); #if defined(STM_RING_BUFFER) stm_rb_post_all(ioc->priv, smid); #endif wait_for_completion_timeout(&ioc->stm_post_cmds.done, STM_CMP_POST_TIMEOUT); if (!(ioc->stm_post_cmds.status & MPT3_CMD_COMPLETE)) { printk(MPT3SAS_ERR_FMT "%s: timeout\n", ioc->stm_name, __func__); if (!(ioc->stm_post_cmds.status & MPT3_CMD_RESET)) issue_reset = 1; goto out; } if (ioc->stm_post_cmds.status & MPT3_CMD_REPLY_VALID) { reply = (Mpi2TargetCmdBufferPostBaseListReply_t *) ioc->stm_post_cmds.reply; /* normall we drop here */ printk(MPT3SAS_INFO_FMT "%s: io_index(0x%04x), smid(%d), " "ioc_status(0x%04x), loginfo(0x%08x), flags(0x%02x)\n", ioc->stm_name, __func__, le16_to_cpu(reply->IoIndex), smid, le16_to_cpu(reply->IOCStatus), le32_to_cpu(reply->IOCLogInfo), reply->Flags); } else { /* TODO anything we need to handle this case ?? */ printk(MPT3SAS_INFO_FMT "%s, smid(%d), no reply\n", ioc->stm_name, __func__, smid); } out: ioc->stm_post_cmds.status = MPT3_CMD_NOT_USED; if (issue_reset) { mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); rc = -EFAULT; } if (ioc->stm_post_cmds.smid == smid) { //mpt3sas_stm_free_smid(ioc, smid); mpt3sas_base_free_smid(ioc, smid); ioc->stm_post_cmds.smid = USHORT_MAX; } return rc; } /** * _stm_thread - thread running from user context * @arg: the io_cmd * */ static int _stm_thread(void *arg) { struct CMD *io_cmd = (struct CMD *)arg; u16 io_state = (IO_STATE_RESET | IO_STATE_POSTED); unsigned long flags; set_user_nice(current, -20); /* set most favorable scheduling */ while (1) { set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&io_cmd->io_state_lock, flags); if (io_cmd->io_state & io_state) { spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); schedule(); spin_lock_irqsave(&io_cmd->io_state_lock, flags); } __set_current_state(TASK_RUNNING); spin_unlock_irqrestore(&io_cmd->io_state_lock, flags); if (kthread_should_stop()) break; switch (io_cmd->cmd[0]) { case SSP_CMD_FRAME: _stm_process_ssp_cmd_frame(io_cmd); break; case SSP_TASK_FRAME: _stm_process_task_cmd_frame(io_cmd); break; } } printk(KERN_INFO "killing stm_io/%d\n", io_cmd->io_index); return 0; } /** * _stm_thread_start - start user context work thread * @priv: target private data * * Creates kernel threads and private memory content for every IO */ static void _stm_thread_start(struct MPT3SAS_ADAPTER *ioc, struct MPT_STM_PRIV *priv) { struct CMD *io_cmd = priv->cmd_buffer; u16 i; priv->stopping_threads = 0; for (i = 0; i < priv->num_cmd_buffers; i++, io_cmd++) { io_cmd->cmd_thread = kthread_run(_stm_thread, io_cmd, "stm_io/%d", i); if (IS_ERR(io_cmd->cmd_thread)) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); } } } /** * _stm_thread_stop - stop user context work thread * @ioc: per adapter object * @priv: target private data * * Free's kernel threads and private memory content for every IO */ static void _stm_thread_stop(struct MPT3SAS_ADAPTER *ioc, struct MPT_STM_PRIV *priv) { struct CMD *io_cmd = priv->cmd_buffer; int i; priv->stopping_threads = 1; if (!io_cmd) return; for (i = 0; i < priv->num_cmd_buffers; i++, io_cmd++) { if (io_cmd->cmd_thread) { kthread_stop(io_cmd->cmd_thread); io_cmd->cmd_thread = NULL; } } } /** * _stm_free_disk - free memory associated to the ramdisk * @ioc: per adapter object * @priv: target private data * @lun_num: */ static void _stm_free_disk(struct MPT3SAS_ADAPTER *ioc, struct MPT_STM_PRIV *priv, u64 lun_num) { struct dma_access *slice; struct DISK *p_disk = priv->luns[lun_num]; int i, j = 0; if (!p_disk) return; if (!p_disk->slice) goto skip; slice = p_disk->slice; for (i = 0; i < p_disk->max_slices; i++, slice++) { if (!slice->va) continue; dma_free_coherent(&ioc->pdev->dev, p_disk->bytes_per_slice, slice->va, slice->pa); j++; } printk(MPT3SAS_INFO_FMT "FREE: lun(%lld), slices_allocated(%d)\n", ioc->stm_name, (unsigned long long)lun_num, j); skip: if (p_disk->data.va) { printk(MPT3SAS_INFO_FMT "FREE: lun(%lld), data.va(%p)\n", ioc->stm_name, (unsigned long long)lun_num, p_disk->data.va); dma_free_coherent(&ioc->pdev->dev, ECHO_BYTES, p_disk->data.va, p_disk->data.pa); } if (p_disk->slice) { printk(MPT3SAS_INFO_FMT "FREE: lun(%lld), slice(%p)\n", ioc->stm_name, (unsigned long long)lun_num, p_disk->slice); free_pages((ulong)p_disk->slice, p_disk->pages); } kfree(p_disk); priv->luns[lun_num] = NULL; } /** * _stm_allocate_disk - allocate memory associated to the ramdisk * @ioc: per adapter object * @priv: target private data * @lun_num: */ static int _stm_allocate_disk(struct MPT3SAS_ADAPTER *ioc, struct MPT_STM_PRIV *priv, u32 lun_num) { struct DISK *p_disk; u32 allocated_memory = 0; u32 allocated_memory_pd = 0; int rc = 0; int sz; /* allocate memory for pdisk */ p_disk = kzalloc(sizeof(struct DISK), GFP_KERNEL); if (!p_disk) { printk(MPT3SAS_ERR_FMT "allocation failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); rc = -1; goto done; } allocated_memory += sizeof(struct DISK); priv->luns[lun_num] = p_disk; mutex_init(&p_disk->data_mutex); p_disk->io_index_locked_for_xfer = USHORT_MAX; p_disk->reservations_handle = STM_NO_RESERVATIONS; p_disk->block_size = BLOCK_LENGTH; p_disk->bytes_per_slice = BYTES_PER_SLICE; p_disk->blocks_per_slice = BLOCKS_PER_SLICE; p_disk->blk_cnt = num_blocks; p_disk->max_slices = p_disk->blk_cnt; do_div(p_disk->max_slices, BLOCKS_PER_SLICE); if (num_blocks % BLOCKS_PER_SLICE) p_disk->max_slices++; printk(MPT3SAS_INFO_FMT "lun(%d), blk_cnt(%d), block_size(%d), " "bytes_per_slice(%d), max_slices(%lld)\n", ioc->stm_name, lun_num, (int)p_disk->blk_cnt, p_disk->block_size, p_disk->bytes_per_slice, (unsigned long long)p_disk->max_slices); /* allocate memory for ramdisk list of pointers to dma memory */ sz = p_disk->max_slices * sizeof(struct dma_access); p_disk->pages = get_order(sz); p_disk->slice = (struct dma_access *)__get_free_pages(GFP_KERNEL, p_disk->pages); if (!p_disk->slice) { printk(MPT3SAS_ERR_FMT "allocation failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); rc = -2; goto done; } memset(p_disk->slice, 0, sz); allocated_memory += sz; /* alloc memory for non-ramdisk request, like inquiries, mode pages */ p_disk->data.va = dma_alloc_coherent(&ioc->pdev->dev, ECHO_BYTES, &p_disk->data.pa, GFP_KERNEL); if (!p_disk->data.va) { printk(MPT3SAS_ERR_FMT "allocation failure at " "%s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); rc = -3; goto done; } allocated_memory_pd = ECHO_BYTES; done: printk("\tallocated_memory(%d) allocated_memory_pd(%d)\n", allocated_memory, allocated_memory_pd); if (rc != 0) printk(MPT3SAS_ERR_FMT "%s: failed!!!\n", ioc->stm_name, __func__); return rc; } /** * _stm_initialize_io_cmd - initialize the per command buffer struct * @ioc: per adapter object */ static void _stm_initialize_io_cmd(struct MPT3SAS_ADAPTER *ioc) { struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; u16 i; void *cmd = priv->cmd.va; void *reply = priv->reply; void *response = priv->response.va; dma_addr_t response_pa = priv->response.pa; io_cmd = priv->cmd_buffer; for (i = 0; i < priv->num_cmd_buffers; i++, io_cmd++) { io_cmd->io_index = i; io_cmd->ioc = ioc; io_cmd->status = MPT3_CMD_NOT_USED; spin_lock_init(&io_cmd->status_lock); io_cmd->tag = INVALID_TAG; io_cmd->tm_tag = INVALID_TAG; io_cmd->smid = USHORT_MAX; spin_lock_init(&io_cmd->io_state_lock); io_cmd->cmd = cmd; cmd += CMD_IU_SIZE; io_cmd->reply = reply; reply += ioc->reply_sz; io_cmd->response.va = response; response += CMD_RESPONSE_SIZE; io_cmd->response.pa = response_pa; response_pa += CMD_RESPONSE_SIZE; } } /** * _stm_configure_target_mode - turn on target mode support in controller FW * @ioc: per adapter object */ static void _stm_configure_target_mode(struct MPT3SAS_ADAPTER *ioc) { Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; int sas_iounit_pg1_length; Mpi2ConfigReply_t mpi_reply; u16 ioc_status; int i; u32 device_info; if (initialize_target_mode == -1) return; sas_iounit_pg1_length = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * sizeof(Mpi2SasIOUnit1PhyData_t)); sas_iounit_pg1 = kzalloc(sas_iounit_pg1_length, GFP_KERNEL); if (!sas_iounit_pg1) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); goto out; } if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sas_iounit_pg1_length))) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); goto out; } ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); goto out; } for (i = 0; i < ioc->sas_hba.num_phys; i++) { sas_iounit_pg1->PhyData[i].Port = i / 4; sas_iounit_pg1->PhyData[i].PortFlags &= ~MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG; sas_iounit_pg1->PhyData[i].PhyFlags &= ~MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED; sas_iounit_pg1->PhyData[i].PhyFlags &= ~MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED; device_info = cpu_to_le32(sas_iounit_pg1->PhyData[i]. ControllerPhyDeviceInfo); device_info |= (MPI2_SAS_DEVICE_INFO_SSP_INITIATOR | MPI2_SAS_DEVICE_INFO_STP_INITIATOR | MPI2_SAS_DEVICE_INFO_SMP_INITIATOR | MPI2_SAS_DEVICE_INFO_SSP_TARGET); sas_iounit_pg1->PhyData[i].ControllerPhyDeviceInfo = le32_to_cpu(device_info); } mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sas_iounit_pg1_length); /* issue reset so new settings are picked up */ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: kfree(sas_iounit_pg1); } /** * mpt3sas_stm_adapter_install - main entry point to attach the controller * @ioc: per adapter object */ void mpt3sas_stm_adapter_install(struct MPT3SAS_ADAPTER *ioc) { struct MPT_STM_PRIV *priv = NULL; u64 lun_num; int sz; if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET)) return; /* doesn't support target mode */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) /* waiting for the port enable to complete */ do { if (ioc->start_scan_failed) { printk(MPT3SAS_INFO_FMT "port enable failed\n", ioc->name); return; } ssleep(1); } while (ioc->start_scan); #endif _stm_configure_target_mode(ioc); sprintf(ioc->stm_name, "stm%d", ioc->id); ioc->stm_io_cb_idx = stm_io_cb_idx; ioc->stm_tm_cb_idx = stm_tm_cb_idx; ioc->stm_post_cb_idx = stm_post_cb_idx; ioc->stm_tm_imm_cb_idx = stm_tm_imm_cb_idx; /* allocate per host private area */ ioc->priv = kzalloc(sizeof(struct MPT_STM_PRIV), GFP_KERNEL); if (!ioc->priv) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); goto fail_out; } priv = ioc->priv; /* calculate command buffer size */ if (!ioc->pfacts[0].MaxPostedCmdBuffers) { printk(MPT3SAS_INFO_FMT "port facts is reporting there are no" "available command buffers\n", ioc->stm_name); printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); goto fail_out; } priv->num_cmd_buffers = NUM_CMD_BUFFERS; if (priv->num_cmd_buffers > ioc->pfacts[0].MaxPostedCmdBuffers) priv->num_cmd_buffers = ioc->pfacts[0].MaxPostedCmdBuffers; /* allocate per command buffer private area */ sz = priv->num_cmd_buffers * sizeof(struct CMD); priv->cmd_buffer_pages = get_order(sz); priv->cmd_buffer = (struct CMD *)__get_free_pages(GFP_KERNEL, priv->cmd_buffer_pages); if (!priv->cmd_buffer) { printk(MPT3SAS_INFO_FMT "priv->num_cmd_buffers(%d), size(%d)\n", ioc->stm_name, priv->num_cmd_buffers, sz); printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); goto fail_out; } memset(priv->cmd_buffer, 0, sz); spin_lock_init(&priv->cmd_buffer_lock); printk(MPT3SAS_INFO_FMT "priv->num_cmd_buffers(%d), " "priv->cmd_buffer(%p), size(%ld)\n", ioc->stm_name, priv->num_cmd_buffers, priv->cmd_buffer, (unsigned long)(priv->num_cmd_buffers * (sizeof(struct CMD)))); /* allocate memory for command buffers */ priv->cmd_sz = priv->num_cmd_buffers * CMD_IU_SIZE; priv->cmd.va = pci_alloc_consistent(ioc->pdev, priv->cmd_sz, &priv->cmd.pa); if (!priv->cmd.va) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); goto fail_out; } memset(priv->cmd.va, 0, priv->cmd_sz); printk(MPT3SAS_INFO_FMT "priv->cmd: va(%p), " "pa(0x%llx), size(%d)\n", ioc->stm_name, priv->cmd.va, (unsigned long long)priv->cmd.pa, priv->cmd_sz); /* allocate memory for response data */ priv->response_sz = priv->num_cmd_buffers * CMD_RESPONSE_SIZE; priv->response.va = pci_alloc_consistent(ioc->pdev, priv->response_sz, &priv->response.pa); if (!priv->response.va) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); goto fail_out; } memset(priv->response.va, 0, priv->response_sz); printk(MPT3SAS_INFO_FMT "priv->response: va(%p), " "pa(0x%llx), size(%d)\n", ioc->stm_name, priv->response.va, (unsigned long long)priv->response.pa, priv->response_sz); /* allocate per command reply frame */ priv->reply = kcalloc(priv->num_cmd_buffers, ioc->reply_sz, GFP_KERNEL); if (!priv->reply) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); goto fail_out; } printk(MPT3SAS_INFO_FMT "priv->reply(%p), size(%d)\n", ioc->stm_name, priv->reply, priv->num_cmd_buffers * ioc->reply_sz); _stm_initialize_io_cmd(ioc); /* allocate memory for ramdisk */ if (num_luns < 0) num_luns = DEFAULT_NUM_LUNS; if (num_luns > MAX_NUM_LUNS) num_luns = MAX_NUM_LUNS; /* setting the number of blocks per lun from command line option */ if (num_blocks == -1) num_blocks = NUM_BLOCKS; else if (num_blocks < NUM_BLOCKS_MIN) { printk(MPT3SAS_WARN_FMT "Invalid value %d passed " "for num_blocks, range is %d to %d. Assigning " "value of %d.\n", ioc->stm_name, num_blocks, BLOCKS_PER_SLICE, NUM_BLOCKS_MAX, NUM_BLOCKS_MIN); num_blocks = NUM_BLOCKS_MIN; } else if (num_blocks > NUM_BLOCKS_MAX) { printk(MPT3SAS_WARN_FMT "Invalid value %d passed " "for num_blocks, range is %d to %d. Assigning " "value of %d.\n", ioc->stm_name, num_blocks, BLOCKS_PER_SLICE, NUM_BLOCKS_MAX, NUM_BLOCKS_MAX); num_blocks = NUM_BLOCKS_MAX; } else printk(MPT3SAS_INFO_FMT "The num_blocks value is " "set to %d\n", ioc->stm_name, num_blocks); for (lun_num = 0; lun_num < num_luns; lun_num++) { if (_stm_allocate_disk(ioc, priv, lun_num) != 0) goto fail_out; } #if defined(STM_RING_BUFFER) sz = STM_RB_MAXSIZE * sizeof(struct stm_rb_element); priv->rb_pages = get_order(sz); priv->ring_buffer = (struct stm_rb_element *) __get_free_pages(GFP_KERNEL, priv->rb_pages); if (!priv->ring_buffer) { printk(MPT3SAS_INFO_FMT "failed allocating ring_buffer, " "size(%d)\n", ioc->stm_name, sz); printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->stm_name, __FILE__, __LINE__, __func__); goto fail_out; } memset(priv->ring_buffer, 0, sz); #endif /* task managment */ ioc->stm_tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->stm_tm_cmds.status = MPT3_CMD_NOT_USED; mutex_init(&ioc->stm_tm_cmds.mutex); /* task managment */ ioc->stm_post_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->stm_post_cmds.status = MPT3_CMD_NOT_USED; _stm_cmd_buf_post_base(ioc, priv); _stm_thread_start(ioc, priv); return; fail_out: if (ioc->priv) { priv = ioc->priv; ioc->priv = NULL; } if (priv) { for (lun_num = 0; lun_num < num_luns; lun_num++) _stm_free_disk(ioc, priv, lun_num); if (priv->cmd.va) pci_free_consistent(ioc->pdev, priv->cmd_sz, priv->cmd.va, priv->cmd.pa); if (priv->response.va) pci_free_consistent(ioc->pdev, priv->response_sz, priv->response.va, priv->response.pa); if (priv->cmd_buffer) free_pages((ulong)priv->cmd_buffer, priv->cmd_buffer_pages); #if defined(STM_RING_BUFFER) if (priv->ring_buffer) free_pages((ulong)priv->ring_buffer, priv->rb_pages); #endif kfree(priv->reply); } kfree(priv); } /** * mpt3sas_stm_adapter_dispose - main entry point to remove the controller * @ioc: per adapter object */ void mpt3sas_stm_adapter_dispose(struct MPT3SAS_ADAPTER *ioc) { u64 lun_number; struct MPT_STM_PRIV *priv = ioc->priv; if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET)) return; /* doesn't support target mode */ if (!priv) goto out; if (mpt3sas_base_get_iocstate(ioc, 1) == MPI2_IOC_STATE_OPERATIONAL) _stm_target_mode_abort(ioc, priv); _stm_thread_stop(ioc, priv); /* deallocate per command reply frame */ printk(MPT3SAS_INFO_FMT "FREE: priv->reply(%p), size(%d)\n", ioc->stm_name, priv->reply, priv->num_cmd_buffers * ioc->reply_sz); kfree(priv->reply); /* deallocate memory for command buffers */ if (priv->cmd.va) { printk(MPT3SAS_INFO_FMT "FREE: priv->cmd: va(%p), " "pa(0x%llx), size(%d)\n", ioc->stm_name, priv->cmd.va, (unsigned long long)priv->cmd.pa, priv->cmd_sz); pci_free_consistent(ioc->pdev, priv->cmd_sz, priv->cmd.va, priv->cmd.pa); } /* deallocate memory for response data */ if (priv->response.va) { printk(MPT3SAS_INFO_FMT "FREE: priv->response: va(%p), " "pa(0x%llx), size(%d)\n", ioc->stm_name, priv->response.va, (unsigned long long)priv->response.pa, priv->response_sz); pci_free_consistent(ioc->pdev, priv->response_sz, priv->response.va, priv->response.pa); } /* allocate memory for ramdisk */ for (lun_number = 0; lun_number < num_luns; lun_number++) _stm_free_disk(ioc, priv, lun_number); /* deallocate per command buffer private area */ if (priv->cmd_buffer) { printk(MPT3SAS_INFO_FMT "FREE: priv->cmd_buffer(%p)\n", ioc->stm_name, priv->cmd_buffer); free_pages((ulong)priv->cmd_buffer, priv->cmd_buffer_pages); } #if defined(STM_RING_BUFFER) if (priv->ring_buffer) { printk(MPT3SAS_INFO_FMT "FREE: priv->ring_buffer(%p)\n", ioc->stm_name, priv->ring_buffer); free_pages((ulong)priv->ring_buffer, priv->rb_pages); } #endif kfree(ioc->stm_tm_cmds.reply); kfree(ioc->stm_post_cmds.reply); out: kfree(priv); } /** * mpt3sas_stm_reset_handler - reset callback handler (for stm) * @ioc: per adapter object * @reset_phase: phase * * The handler for doing any required cleanup or initialization. * * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET, * MPT3_IOC_DONE_RESET * * Return nothing. */ void mpt3sas_stm_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) { struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; int i; switch (reset_phase) { case MPT3_IOC_PRE_RESET: dtmprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: " "MPT3_IOC_PRE_RESET\n", ioc->stm_name, __func__)); break; case MPT3_IOC_AFTER_RESET: dtmprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: " "MPT3_IOC_AFTER_RESET\n", ioc->stm_name, __func__)); io_cmd = priv->cmd_buffer; for (i = 0; i < priv->num_cmd_buffers; i++, io_cmd++) { io_cmd->out_of_order_cmp = 0; _stm_set_io_state(io_cmd, IO_STATE_RESET); if (_stm_check_io_state(io_cmd, IO_STATE_TARGET_ASSIST_STATUS_PEND | IO_STATE_TARGET_ASSIST_DATA_PEND) == 1) { if (io_cmd->smid != USHORT_MAX) { //mpt3sas_stm_free_smid(ioc, // io_cmd->smid); mpt3sas_base_free_smid(ioc, io_cmd->smid); io_cmd->smid = USHORT_MAX; } complete(&io_cmd->done); } } if (ioc->stm_tm_cmds.status & MPT3_CMD_PENDING) { ioc->stm_tm_cmds.status |= MPT3_CMD_RESET; complete(&ioc->stm_tm_cmds.done); } if (ioc->stm_post_cmds.status & MPT3_CMD_PENDING) { ioc->stm_post_cmds.status |= MPT3_CMD_RESET; complete(&ioc->stm_post_cmds.done); } break; case MPT3_IOC_DONE_RESET: _stm_cmd_buf_post_base(ioc, priv); dtmprintk(ioc, printk(MPT3SAS_INFO_FMT "%s: " "MPT3_IOC_DONE_RESET\n", ioc->stm_name, __func__)); break; } } void mpt3sas_stm_watchdog(struct MPT3SAS_ADAPTER *ioc) { struct MPT_STM_PRIV *priv = ioc->priv; struct CMD *io_cmd; int i; if (priv == NULL || priv->cmd_buffer == NULL || priv->stopping_threads) return; for (i = 0, io_cmd = priv->cmd_buffer; i < priv->num_cmd_buffers; i++, io_cmd++) { if (priv->stopping_threads) continue; /* handle target assist that have been idle for long time */ if (_stm_check_io_state(io_cmd, IO_STATE_TARGET_ASSIST_DATA_PEND) == 1) { if (io_cmd->ta_wait_count++ < 75) continue; #if defined(TM_ERROR_HANDLING_DEBUG) printk(MPT3SAS_INFO_FMT "ABORTING_STALE_TA: " "io_index(0x%04x), tag(0x%04x)\n", ioc->stm_name, io_cmd->io_index, io_cmd->tag); #endif _stm_target_mode_abort_io_request_and_io(ioc, io_cmd, io_cmd->smid); } } } /** * mpt3sas_stm_init - main entry point when loading the driver */ #ifdef TARGET_MODE int #else static int __init #endif mpt3sas_stm_init(void) { int i; struct MPT3SAS_ADAPTER *ioc; struct STM_CALLBACK stm_funcs; stm_io_cb_idx = mpt3sas_base_register_callback_handler(_stm_io_done); printk(KERN_INFO "stm: stm_io_cb_idx is assigned context(0x%02x)\n", stm_io_cb_idx); stm_tm_cb_idx = mpt3sas_base_register_callback_handler(_stm_tm_done); printk(KERN_INFO "stm: stm_tm_cb_idx is assigned context(0x%02x)\n", stm_tm_cb_idx); stm_tm_imm_cb_idx = mpt3sas_base_register_callback_handler( _stm_target_mode_abort_imm_done); printk(KERN_INFO "stm: stm_tm_imm_cb_idx is assigned context(0x%02x)\n", stm_tm_imm_cb_idx); stm_post_cb_idx = mpt3sas_base_register_callback_handler(_stm_post_done); printk(KERN_INFO "stm: stm_post_cb_idx is assigned context(0x%02x)\n", stm_post_cb_idx); list_for_each_entry(ioc, &mpt3sas_ioc_list, list) mpt3sas_stm_adapter_install(ioc); stm_funcs.watchdog = mpt3sas_stm_watchdog; stm_funcs.target_command = mpt3sas_stmapp_target_command; stm_funcs.target_assist = mpt3sas_stm_target_assist_success_reply; stm_funcs.smid_handler = mpt3sas_stm_zero_smid_handler; stm_funcs.reset_handler = mpt3sas_stm_reset_handler; mpt3sas_base_stm_register_callback_handler(stm_funcs); /* setting up product indentification for target inquiry */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) strncpy(mpt_stm_hostname, utsname()->nodename, 16); #else strncpy(mpt_stm_hostname, system_utsname.nodename, 16); #endif if (strcmp(mpt_stm_hostname, "localhost") == 0) mpt_stm_hostname[0] = '\0'; for (i = 0; i < 16; i++) if (mpt_stm_hostname[i] == '\0') break; for (; i < 16; i++) mpt_stm_hostname[i] = ' '; mpt_stm_hostname[i] = '\0'; list_for_each_entry(ioc, &mpt3sas_ioc_list, list) scsi_scan_host(ioc->shost); return 0; } /** * mpt3sas_stm_exit - Exit point when unloading the driver */ #ifdef TARGET_MODE void #else static void __exit #endif mpt3sas_stm_exit(void) { struct MPT3SAS_ADAPTER *ioc; list_for_each_entry(ioc, &mpt3sas_ioc_list, list) mpt3sas_stm_adapter_dispose(ioc); mpt3sas_base_release_callback_handler(stm_tm_imm_cb_idx); mpt3sas_base_release_callback_handler(stm_tm_cb_idx); mpt3sas_base_release_callback_handler(stm_io_cb_idx); mpt3sas_base_stm_release_callback_handler(); } #ifdef TARGET_MODE #else module_init(mpt3sas_stm_init); module_exit(mpt3sas_stm_exit); #endif #endif /* defined(TARGET_MODE) */