diff options
author | James Smart <james.smart@emulex.com> | 2014-02-20 09:56:45 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2014-03-15 10:18:56 -0700 |
commit | 1ba981fd3ad1f91b8bb205ce6aac6aad45f2fa7a (patch) | |
tree | c5638d855f177a4be300a2df71d8d621a2cfc0ca /drivers/scsi/lpfc/lpfc_scsi.c | |
parent | 2a2719d370b2ec31451462f2775986e42cbeb39f (diff) | |
download | op-kernel-dev-1ba981fd3ad1f91b8bb205ce6aac6aad45f2fa7a.zip op-kernel-dev-1ba981fd3ad1f91b8bb205ce6aac6aad45f2fa7a.tar.gz |
[SCSI] lpfc 8.3.45: Incorporated support of a low-latency io path
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_scsi.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 406 |
1 files changed, 398 insertions, 8 deletions
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 0b08188..4015fcc 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -68,6 +68,17 @@ struct scsi_dif_tuple { __be32 ref_tag; /* Target LBA or indirect LBA */ }; +static struct lpfc_rport_data * +lpfc_rport_data_from_scsi_device(struct scsi_device *sdev) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)sdev->host->hostdata; + + if (vport->phba->cfg_EnableXLane) + return ((struct lpfc_device_data *)sdev->hostdata)->rport_data; + else + return (struct lpfc_rport_data *)sdev->hostdata; +} + static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); static void @@ -306,7 +317,7 @@ lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) old_queue_depth = sdev->queue_depth; scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); new_queue_depth = sdev->queue_depth; - rdata = sdev->hostdata; + rdata = lpfc_rport_data_from_scsi_device(sdev); if (rdata) lpfc_send_sdev_queuedepth_change_event(phba, vport, rdata->pnode, sdev->lun, @@ -1502,7 +1513,7 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, } /* Next check if we need to match the remote NPortID or WWPN */ - rdata = sc->device->hostdata; + rdata = lpfc_rport_data_from_scsi_device(sc->device); if (rdata && rdata->pnode) { ndlp = rdata->pnode; @@ -3507,6 +3518,14 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) * we need to set word 4 of IOCB here */ iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd); + + /* + * If the OAS driver feature is enabled and the lun is enabled for + * OAS, set the oas iocb related flags. + */ + if ((phba->cfg_EnableXLane) && ((struct lpfc_device_data *) + scsi_cmnd->device->hostdata)->oas_enabled) + lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_OAS; return 0; } @@ -4691,12 +4710,13 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) { struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *ndlp; struct lpfc_scsi_buf *lpfc_cmd; struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); int err; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); err = fc_remote_port_chkready(rport); if (err) { cmnd->result = err; @@ -5179,10 +5199,11 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, static int lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd) { - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned long later; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) { lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "0797 Tgt Map rport failure: rdata x%p\n", rdata); @@ -5200,7 +5221,7 @@ lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd) if (pnode->nlp_state == NLP_STE_MAPPED_NODE) return SUCCESS; schedule_timeout_uninterruptible(msecs_to_jiffies(500)); - rdata = cmnd->device->hostdata; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) return FAILED; pnode = rdata->pnode; @@ -5272,13 +5293,14 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) { struct Scsi_Host *shost = cmnd->device->host; struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; unsigned int lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; int status; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "0798 Device Reset rport failure: rdata x%p\n", rdata); @@ -5341,13 +5363,14 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) { struct Scsi_Host *shost = cmnd->device->host; struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; unsigned int lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; int status; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "0799 Target Reset rport failure: rdata x%p\n", rdata); @@ -5547,11 +5570,45 @@ lpfc_slave_alloc(struct scsi_device *sdev) uint32_t num_to_alloc = 0; int num_allocated = 0; uint32_t sdev_cnt; + struct lpfc_device_data *device_data; + unsigned long flags; + struct lpfc_name target_wwpn; if (!rport || fc_remote_port_chkready(rport)) return -ENXIO; - sdev->hostdata = rport->dd_data; + if (phba->cfg_EnableXLane) { + + /* + * Check to see if the device data structure for the lun + * exists. If not, create one. + */ + + u64_to_wwn(rport->port_name, target_wwpn.u.wwn); + spin_lock_irqsave(&phba->devicelock, flags); + device_data = __lpfc_get_device_data(phba, + &phba->luns, + &vport->fc_portname, + &target_wwpn, + sdev->lun); + if (!device_data) { + spin_unlock_irqrestore(&phba->devicelock, flags); + device_data = lpfc_create_device_data(phba, + &vport->fc_portname, + &target_wwpn, + sdev->lun, true); + if (!device_data) + return -ENOMEM; + spin_lock_irqsave(&phba->devicelock, flags); + list_add_tail(&device_data->listentry, &phba->luns); + } + device_data->rport_data = rport->dd_data; + device_data->available = true; + spin_unlock_irqrestore(&phba->devicelock, flags); + sdev->hostdata = device_data; + } else { + sdev->hostdata = rport->dd_data; + } sdev_cnt = atomic_inc_return(&phba->sdev_cnt); /* @@ -5641,11 +5698,344 @@ lpfc_slave_destroy(struct scsi_device *sdev) { struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata; struct lpfc_hba *phba = vport->phba; + unsigned long flags; + struct lpfc_device_data *device_data = sdev->hostdata; + atomic_dec(&phba->sdev_cnt); + if ((phba->cfg_EnableXLane) && (device_data)) { + spin_lock_irqsave(&phba->devicelock, flags); + device_data->available = false; + if (!device_data->oas_enabled) + lpfc_delete_device_data(phba, device_data); + spin_unlock_irqrestore(&phba->devicelock, flags); + } sdev->hostdata = NULL; return; } +/** + * lpfc_create_device_data - creates and initializes device data structure for OAS + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun on target + * @atomic_create: Flag to indicate if memory should be allocated using the + * GFP_ATOMIC flag or not. + * + * This routine creates a device data structure which will contain identifying + * information for the device (host wwpn, target wwpn, lun), state of OAS, + * whether or not the corresponding lun is available by the system, + * and pointer to the rport data. + * + * Return codes: + * NULL - Error + * Pointer to lpfc_device_data - Success + **/ +struct lpfc_device_data* +lpfc_create_device_data(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun, + bool atomic_create) +{ + + struct lpfc_device_data *lun_info; + int memory_flags; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !(phba->cfg_EnableXLane)) + return NULL; + + /* Attempt to create the device data to contain lun info */ + + if (atomic_create) + memory_flags = GFP_ATOMIC; + else + memory_flags = GFP_KERNEL; + lun_info = mempool_alloc(phba->device_data_mem_pool, memory_flags); + if (!lun_info) + return NULL; + INIT_LIST_HEAD(&lun_info->listentry); + lun_info->rport_data = NULL; + memcpy(&lun_info->device_id.vport_wwpn, vport_wwpn, + sizeof(struct lpfc_name)); + memcpy(&lun_info->device_id.target_wwpn, target_wwpn, + sizeof(struct lpfc_name)); + lun_info->device_id.lun = lun; + lun_info->oas_enabled = false; + lun_info->available = false; + return lun_info; +} + +/** + * lpfc_delete_device_data - frees a device data structure for OAS + * @pha: Pointer to host bus adapter structure. + * @lun_info: Pointer to device data structure to free. + * + * This routine frees the previously allocated device data structure passed. + * + **/ +void +lpfc_delete_device_data(struct lpfc_hba *phba, + struct lpfc_device_data *lun_info) +{ + + if (unlikely(!phba) || !lun_info || + !(phba->cfg_EnableXLane)) + return; + + if (!list_empty(&lun_info->listentry)) + list_del(&lun_info->listentry); + mempool_free(lun_info, phba->device_data_mem_pool); + return; +} + +/** + * __lpfc_get_device_data - returns the device data for the specified lun + * @pha: Pointer to host bus adapter structure. + * @list: Point to list to search. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun on target + * + * This routine searches the list passed for the specified lun's device data. + * This function does not hold locks, it is the responsibility of the caller + * to ensure the proper lock is held before calling the function. + * + * Return codes: + * NULL - Error + * Pointer to lpfc_device_data - Success + **/ +struct lpfc_device_data* +__lpfc_get_device_data(struct lpfc_hba *phba, struct list_head *list, + struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun) +{ + + struct lpfc_device_data *lun_info; + + if (unlikely(!phba) || !list || !vport_wwpn || !target_wwpn || + !phba->cfg_EnableXLane) + return NULL; + + /* Check to see if the lun is already enabled for OAS. */ + + list_for_each_entry(lun_info, list, listentry) { + if ((memcmp(&lun_info->device_id.vport_wwpn, vport_wwpn, + sizeof(struct lpfc_name)) == 0) && + (memcmp(&lun_info->device_id.target_wwpn, target_wwpn, + sizeof(struct lpfc_name)) == 0) && + (lun_info->device_id.lun == lun)) + return lun_info; + } + + return NULL; +} + +/** + * lpfc_find_next_oas_lun - searches for the next oas lun + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @starting_lun: Pointer to the lun to start searching for + * @found_vport_wwpn: Pointer to the found lun's vport wwpn information + * @found_target_wwpn: Pointer to the found lun's target wwpn information + * @found_lun: Pointer to the found lun. + * @found_lun_status: Pointer to status of the found lun. + * + * This routine searches the luns list for the specified lun + * or the first lun for the vport/target. If the vport wwpn contains + * a zero value then a specific vport is not specified. In this case + * any vport which contains the lun will be considered a match. If the + * target wwpn contains a zero value then a specific target is not specified. + * In this case any target which contains the lun will be considered a + * match. If the lun is found, the lun, vport wwpn, target wwpn and lun status + * are returned. The function will also return the next lun if available. + * If the next lun is not found, starting_lun parameter will be set to + * NO_MORE_OAS_LUN. + * + * Return codes: + * non-0 - Error + * 0 - Success + **/ +bool +lpfc_find_next_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t *starting_lun, + struct lpfc_name *found_vport_wwpn, + struct lpfc_name *found_target_wwpn, + uint64_t *found_lun, + uint32_t *found_lun_status) +{ + + unsigned long flags; + struct lpfc_device_data *lun_info; + struct lpfc_device_id *device_id; + uint64_t lun; + bool found = false; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !starting_lun || !found_vport_wwpn || + !found_target_wwpn || !found_lun || !found_lun_status || + (*starting_lun == NO_MORE_OAS_LUN) || + !phba->cfg_EnableXLane) + return false; + + lun = *starting_lun; + *found_lun = NO_MORE_OAS_LUN; + *starting_lun = NO_MORE_OAS_LUN; + + /* Search for lun or the lun closet in value */ + + spin_lock_irqsave(&phba->devicelock, flags); + list_for_each_entry(lun_info, &phba->luns, listentry) { + if (((wwn_to_u64(vport_wwpn->u.wwn) == 0) || + (memcmp(&lun_info->device_id.vport_wwpn, vport_wwpn, + sizeof(struct lpfc_name)) == 0)) && + ((wwn_to_u64(target_wwpn->u.wwn) == 0) || + (memcmp(&lun_info->device_id.target_wwpn, target_wwpn, + sizeof(struct lpfc_name)) == 0)) && + (lun_info->oas_enabled)) { + device_id = &lun_info->device_id; + if ((!found) && + ((lun == FIND_FIRST_OAS_LUN) || + (device_id->lun == lun))) { + *found_lun = device_id->lun; + memcpy(found_vport_wwpn, + &device_id->vport_wwpn, + sizeof(struct lpfc_name)); + memcpy(found_target_wwpn, + &device_id->target_wwpn, + sizeof(struct lpfc_name)); + if (lun_info->available) + *found_lun_status = + OAS_LUN_STATUS_EXISTS; + else + *found_lun_status = 0; + if (phba->cfg_oas_flags & OAS_FIND_ANY_VPORT) + memset(vport_wwpn, 0x0, + sizeof(struct lpfc_name)); + if (phba->cfg_oas_flags & OAS_FIND_ANY_TARGET) + memset(target_wwpn, 0x0, + sizeof(struct lpfc_name)); + found = true; + } else if (found) { + *starting_lun = device_id->lun; + memcpy(vport_wwpn, &device_id->vport_wwpn, + sizeof(struct lpfc_name)); + memcpy(target_wwpn, &device_id->target_wwpn, + sizeof(struct lpfc_name)); + break; + } + } + } + spin_unlock_irqrestore(&phba->devicelock, flags); + return found; +} + +/** + * lpfc_enable_oas_lun - enables a lun for OAS operations + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun + * + * This routine enables a lun for oas operations. The routines does so by + * doing the following : + * + * 1) Checks to see if the device data for the lun has been created. + * 2) If found, sets the OAS enabled flag if not set and returns. + * 3) Otherwise, creates a device data structure. + * 4) If successfully created, indicates the device data is for an OAS lun, + * indicates the lun is not available and add to the list of luns. + * + * Return codes: + * false - Error + * true - Success + **/ +bool +lpfc_enable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun) +{ + + struct lpfc_device_data *lun_info; + unsigned long flags; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !phba->cfg_EnableXLane) + return false; + + spin_lock_irqsave(&phba->devicelock, flags); + + /* Check to see if the device data for the lun has been created */ + lun_info = __lpfc_get_device_data(phba, &phba->luns, vport_wwpn, + target_wwpn, lun); + if (lun_info) { + if (!lun_info->oas_enabled) + lun_info->oas_enabled = true; + spin_unlock_irqrestore(&phba->devicelock, flags); + return true; + } + + /* Create an lun info structure and add to list of luns */ + lun_info = lpfc_create_device_data(phba, vport_wwpn, target_wwpn, lun, + false); + if (lun_info) { + lun_info->oas_enabled = true; + lun_info->available = false; + list_add_tail(&lun_info->listentry, &phba->luns); + spin_unlock_irqrestore(&phba->devicelock, flags); + return true; + } + spin_unlock_irqrestore(&phba->devicelock, flags); + return false; +} + +/** + * lpfc_disable_oas_lun - disables a lun for OAS operations + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun + * + * This routine disables a lun for oas operations. The routines does so by + * doing the following : + * + * 1) Checks to see if the device data for the lun is created. + * 2) If present, clears the flag indicating this lun is for OAS. + * 3) If the lun is not available by the system, the device data is + * freed. + * + * Return codes: + * false - Error + * true - Success + **/ +bool +lpfc_disable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun) +{ + + struct lpfc_device_data *lun_info; + unsigned long flags; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !phba->cfg_EnableXLane) + return false; + + spin_lock_irqsave(&phba->devicelock, flags); + + /* Check to see if the lun is available. */ + lun_info = __lpfc_get_device_data(phba, + &phba->luns, vport_wwpn, + target_wwpn, lun); + if (lun_info) { + lun_info->oas_enabled = false; + if (!lun_info->available) + lpfc_delete_device_data(phba, lun_info); + spin_unlock_irqrestore(&phba->devicelock, flags); + return true; + } + + spin_unlock_irqrestore(&phba->devicelock, flags); + return false; +} struct scsi_host_template lpfc_template = { .module = THIS_MODULE, |