162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * QLogic Fibre Channel HBA Driver 462306a36Sopenharmony_ci * Copyright (c) 2003-2014 QLogic Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "qla_def.h" 762306a36Sopenharmony_ci#include "qla_gbl.h" 862306a36Sopenharmony_ci#include "qla_target.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/moduleparam.h> 1162306a36Sopenharmony_ci#include <linux/vmalloc.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 1662306a36Sopenharmony_ci#include <scsi/scsicam.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_civoid 2062306a36Sopenharmony_ciqla2x00_vp_stop_timer(scsi_qla_host_t *vha) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci if (vha->vp_idx && vha->timer_active) { 2362306a36Sopenharmony_ci del_timer_sync(&vha->timer); 2462306a36Sopenharmony_ci vha->timer_active = 0; 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic uint32_t 2962306a36Sopenharmony_ciqla24xx_allocate_vp_id(scsi_qla_host_t *vha) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci uint32_t vp_id; 3262306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 3362306a36Sopenharmony_ci unsigned long flags; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* Find an empty slot and assign an vp_id */ 3662306a36Sopenharmony_ci mutex_lock(&ha->vport_lock); 3762306a36Sopenharmony_ci vp_id = find_first_zero_bit(ha->vp_idx_map, ha->max_npiv_vports + 1); 3862306a36Sopenharmony_ci if (vp_id > ha->max_npiv_vports) { 3962306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xa000, 4062306a36Sopenharmony_ci "vp_id %d is bigger than max-supported %d.\n", 4162306a36Sopenharmony_ci vp_id, ha->max_npiv_vports); 4262306a36Sopenharmony_ci mutex_unlock(&ha->vport_lock); 4362306a36Sopenharmony_ci return vp_id; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci set_bit(vp_id, ha->vp_idx_map); 4762306a36Sopenharmony_ci ha->num_vhosts++; 4862306a36Sopenharmony_ci vha->vp_idx = vp_id; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 5162306a36Sopenharmony_ci list_add_tail(&vha->list, &ha->vp_list); 5262306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci spin_lock_irqsave(&ha->hardware_lock, flags); 5562306a36Sopenharmony_ci qla_update_vp_map(vha, SET_VP_IDX); 5662306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->hardware_lock, flags); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci mutex_unlock(&ha->vport_lock); 5962306a36Sopenharmony_ci return vp_id; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_civoid 6362306a36Sopenharmony_ciqla24xx_deallocate_vp_id(scsi_qla_host_t *vha) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci uint16_t vp_id; 6662306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 6762306a36Sopenharmony_ci unsigned long flags = 0; 6862306a36Sopenharmony_ci u32 i, bailout; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci mutex_lock(&ha->vport_lock); 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * Wait for all pending activities to finish before removing vport from 7362306a36Sopenharmony_ci * the list. 7462306a36Sopenharmony_ci * Lock needs to be held for safe removal from the list (it 7562306a36Sopenharmony_ci * ensures no active vp_list traversal while the vport is removed 7662306a36Sopenharmony_ci * from the queue) 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci bailout = 0; 7962306a36Sopenharmony_ci for (i = 0; i < 500; i++) { 8062306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 8162306a36Sopenharmony_ci if (atomic_read(&vha->vref_count) == 0) { 8262306a36Sopenharmony_ci list_del(&vha->list); 8362306a36Sopenharmony_ci qla_update_vp_map(vha, RESET_VP_IDX); 8462306a36Sopenharmony_ci bailout = 1; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (bailout) 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci msleep(20); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci if (!bailout) { 9462306a36Sopenharmony_ci ql_log(ql_log_info, vha, 0xfffa, 9562306a36Sopenharmony_ci "vha->vref_count=%u timeout\n", vha->vref_count.counter); 9662306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 9762306a36Sopenharmony_ci list_del(&vha->list); 9862306a36Sopenharmony_ci qla_update_vp_map(vha, RESET_VP_IDX); 9962306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci vp_id = vha->vp_idx; 10362306a36Sopenharmony_ci ha->num_vhosts--; 10462306a36Sopenharmony_ci clear_bit(vp_id, ha->vp_idx_map); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci mutex_unlock(&ha->vport_lock); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic scsi_qla_host_t * 11062306a36Sopenharmony_ciqla24xx_find_vhost_by_name(struct qla_hw_data *ha, uint8_t *port_name) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci scsi_qla_host_t *vha; 11362306a36Sopenharmony_ci struct scsi_qla_host *tvha; 11462306a36Sopenharmony_ci unsigned long flags; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 11762306a36Sopenharmony_ci /* Locate matching device in database. */ 11862306a36Sopenharmony_ci list_for_each_entry_safe(vha, tvha, &ha->vp_list, list) { 11962306a36Sopenharmony_ci if (!memcmp(port_name, vha->port_name, WWN_SIZE)) { 12062306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 12162306a36Sopenharmony_ci return vha; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 12562306a36Sopenharmony_ci return NULL; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* 12962306a36Sopenharmony_ci * qla2x00_mark_vp_devices_dead 13062306a36Sopenharmony_ci * Updates fcport state when device goes offline. 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * Input: 13362306a36Sopenharmony_ci * ha = adapter block pointer. 13462306a36Sopenharmony_ci * fcport = port structure pointer. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * Return: 13762306a36Sopenharmony_ci * None. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Context: 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic void 14262306a36Sopenharmony_ciqla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * !!! NOTE !!! 14662306a36Sopenharmony_ci * This function, if called in contexts other than vp create, disable 14762306a36Sopenharmony_ci * or delete, please make sure this is synchronized with the 14862306a36Sopenharmony_ci * delete thread. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci fc_port_t *fcport; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci list_for_each_entry(fcport, &vha->vp_fcports, list) { 15362306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xa001, 15462306a36Sopenharmony_ci "Marking port dead, loop_id=0x%04x : %x.\n", 15562306a36Sopenharmony_ci fcport->loop_id, fcport->vha->vp_idx); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci qla2x00_mark_device_lost(vha, fcport, 0); 15862306a36Sopenharmony_ci qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciint 16362306a36Sopenharmony_ciqla24xx_disable_vp(scsi_qla_host_t *vha) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci unsigned long flags; 16662306a36Sopenharmony_ci int ret = QLA_SUCCESS; 16762306a36Sopenharmony_ci fc_port_t *fcport; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (vha->hw->flags.edif_enabled) { 17062306a36Sopenharmony_ci if (DBELL_ACTIVE(vha)) 17162306a36Sopenharmony_ci qla2x00_post_aen_work(vha, FCH_EVT_VENDOR_UNIQUE, 17262306a36Sopenharmony_ci FCH_EVT_VENDOR_UNIQUE_VPORT_DOWN); 17362306a36Sopenharmony_ci /* delete sessions and flush sa_indexes */ 17462306a36Sopenharmony_ci qla2x00_wait_for_sess_deletion(vha); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (vha->hw->flags.fw_started) 17862306a36Sopenharmony_ci ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci atomic_set(&vha->loop_state, LOOP_DOWN); 18162306a36Sopenharmony_ci atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); 18262306a36Sopenharmony_ci list_for_each_entry(fcport, &vha->vp_fcports, list) 18362306a36Sopenharmony_ci fcport->logout_on_delete = 0; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (!vha->hw->flags.edif_enabled) 18662306a36Sopenharmony_ci qla2x00_wait_for_sess_deletion(vha); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Remove port id from vp target map */ 18962306a36Sopenharmony_ci spin_lock_irqsave(&vha->hw->hardware_lock, flags); 19062306a36Sopenharmony_ci qla_update_vp_map(vha, RESET_AL_PA); 19162306a36Sopenharmony_ci spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci qla2x00_mark_vp_devices_dead(vha); 19462306a36Sopenharmony_ci atomic_set(&vha->vp_state, VP_FAILED); 19562306a36Sopenharmony_ci vha->flags.management_server_logged_in = 0; 19662306a36Sopenharmony_ci if (ret == QLA_SUCCESS) { 19762306a36Sopenharmony_ci fc_vport_set_state(vha->fc_vport, FC_VPORT_DISABLED); 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); 20062306a36Sopenharmony_ci return -1; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciint 20662306a36Sopenharmony_ciqla24xx_enable_vp(scsi_qla_host_t *vha) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci int ret; 20962306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 21062306a36Sopenharmony_ci scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Check if physical ha port is Up */ 21362306a36Sopenharmony_ci if (atomic_read(&base_vha->loop_state) == LOOP_DOWN || 21462306a36Sopenharmony_ci atomic_read(&base_vha->loop_state) == LOOP_DEAD || 21562306a36Sopenharmony_ci !(ha->current_topology & ISP_CFG_F)) { 21662306a36Sopenharmony_ci vha->vp_err_state = VP_ERR_PORTDWN; 21762306a36Sopenharmony_ci fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN); 21862306a36Sopenharmony_ci ql_dbg(ql_dbg_taskm, vha, 0x800b, 21962306a36Sopenharmony_ci "%s skip enable. loop_state %x topo %x\n", 22062306a36Sopenharmony_ci __func__, base_vha->loop_state.counter, 22162306a36Sopenharmony_ci ha->current_topology); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci goto enable_failed; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Initialize the new vport unless it is a persistent port */ 22762306a36Sopenharmony_ci mutex_lock(&ha->vport_lock); 22862306a36Sopenharmony_ci ret = qla24xx_modify_vp_config(vha); 22962306a36Sopenharmony_ci mutex_unlock(&ha->vport_lock); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (ret != QLA_SUCCESS) { 23262306a36Sopenharmony_ci fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); 23362306a36Sopenharmony_ci goto enable_failed; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ql_dbg(ql_dbg_taskm, vha, 0x801a, 23762306a36Sopenharmony_ci "Virtual port with id: %d - Enabled.\n", vha->vp_idx); 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cienable_failed: 24162306a36Sopenharmony_ci ql_dbg(ql_dbg_taskm, vha, 0x801b, 24262306a36Sopenharmony_ci "Virtual port with id: %d - Disabled.\n", vha->vp_idx); 24362306a36Sopenharmony_ci return 1; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void 24762306a36Sopenharmony_ciqla24xx_configure_vp(scsi_qla_host_t *vha) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct fc_vport *fc_vport; 25062306a36Sopenharmony_ci int ret; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci fc_vport = vha->fc_vport; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xa002, 25562306a36Sopenharmony_ci "%s: change request #3.\n", __func__); 25662306a36Sopenharmony_ci ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx); 25762306a36Sopenharmony_ci if (ret != QLA_SUCCESS) { 25862306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xa003, "Failed to enable " 25962306a36Sopenharmony_ci "receiving of RSCN requests: 0x%x.\n", ret); 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci } else { 26262306a36Sopenharmony_ci /* Corresponds to SCR enabled */ 26362306a36Sopenharmony_ci clear_bit(VP_SCR_NEEDED, &vha->vp_flags); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci vha->flags.online = 1; 26762306a36Sopenharmony_ci if (qla24xx_configure_vhba(vha)) 26862306a36Sopenharmony_ci return; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci atomic_set(&vha->vp_state, VP_ACTIVE); 27162306a36Sopenharmony_ci fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid 27562306a36Sopenharmony_ciqla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci scsi_qla_host_t *vha, *tvp; 27862306a36Sopenharmony_ci struct qla_hw_data *ha = rsp->hw; 27962306a36Sopenharmony_ci int i = 0; 28062306a36Sopenharmony_ci unsigned long flags; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 28362306a36Sopenharmony_ci list_for_each_entry_safe(vha, tvp, &ha->vp_list, list) { 28462306a36Sopenharmony_ci if (vha->vp_idx) { 28562306a36Sopenharmony_ci if (test_bit(VPORT_DELETE, &vha->dpc_flags)) 28662306a36Sopenharmony_ci continue; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci atomic_inc(&vha->vref_count); 28962306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci switch (mb[0]) { 29262306a36Sopenharmony_ci case MBA_LIP_OCCURRED: 29362306a36Sopenharmony_ci case MBA_LOOP_UP: 29462306a36Sopenharmony_ci case MBA_LOOP_DOWN: 29562306a36Sopenharmony_ci case MBA_LIP_RESET: 29662306a36Sopenharmony_ci case MBA_POINT_TO_POINT: 29762306a36Sopenharmony_ci case MBA_CHG_IN_CONNECTION: 29862306a36Sopenharmony_ci ql_dbg(ql_dbg_async, vha, 0x5024, 29962306a36Sopenharmony_ci "Async_event for VP[%d], mb=0x%x vha=%p.\n", 30062306a36Sopenharmony_ci i, *mb, vha); 30162306a36Sopenharmony_ci qla2x00_async_event(vha, rsp, mb); 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case MBA_PORT_UPDATE: 30462306a36Sopenharmony_ci case MBA_RSCN_UPDATE: 30562306a36Sopenharmony_ci if ((mb[3] & 0xff) == vha->vp_idx) { 30662306a36Sopenharmony_ci ql_dbg(ql_dbg_async, vha, 0x5024, 30762306a36Sopenharmony_ci "Async_event for VP[%d], mb=0x%x vha=%p\n", 30862306a36Sopenharmony_ci i, *mb, vha); 30962306a36Sopenharmony_ci qla2x00_async_event(vha, rsp, mb); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 31562306a36Sopenharmony_ci atomic_dec(&vha->vref_count); 31662306a36Sopenharmony_ci wake_up(&vha->vref_waitq); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci i++; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciint 32462306a36Sopenharmony_ciqla2x00_vp_abort_isp(scsi_qla_host_t *vha) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci fc_port_t *fcport; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * To exclusively reset vport, we need to log it out first. 33062306a36Sopenharmony_ci * Note: This control_vp can fail if ISP reset is already 33162306a36Sopenharmony_ci * issued, this is expected, as the vp would be already 33262306a36Sopenharmony_ci * logged out due to ISP reset. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { 33562306a36Sopenharmony_ci qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); 33662306a36Sopenharmony_ci list_for_each_entry(fcport, &vha->vp_fcports, list) 33762306a36Sopenharmony_ci fcport->logout_on_delete = 0; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * Physical port will do most of the abort and recovery work. We can 34262306a36Sopenharmony_ci * just treat it as a loop down 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci if (atomic_read(&vha->loop_state) != LOOP_DOWN) { 34562306a36Sopenharmony_ci atomic_set(&vha->loop_state, LOOP_DOWN); 34662306a36Sopenharmony_ci qla2x00_mark_all_devices_lost(vha); 34762306a36Sopenharmony_ci } else { 34862306a36Sopenharmony_ci if (!atomic_read(&vha->loop_down_timer)) 34962306a36Sopenharmony_ci atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci ql_dbg(ql_dbg_taskm, vha, 0x801d, 35362306a36Sopenharmony_ci "Scheduling enable of Vport %d.\n", vha->vp_idx); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return qla24xx_enable_vp(vha); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int 35962306a36Sopenharmony_ciqla2x00_do_dpc_vp(scsi_qla_host_t *vha) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 36262306a36Sopenharmony_ci scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x4012, 36562306a36Sopenharmony_ci "Entering %s vp_flags: 0x%lx.\n", __func__, vha->vp_flags); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Check if Fw is ready to configure VP first */ 36862306a36Sopenharmony_ci if (test_bit(VP_CONFIG_OK, &base_vha->vp_flags)) { 36962306a36Sopenharmony_ci if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) { 37062306a36Sopenharmony_ci /* VP acquired. complete port configuration */ 37162306a36Sopenharmony_ci ql_dbg(ql_dbg_dpc, vha, 0x4014, 37262306a36Sopenharmony_ci "Configure VP scheduled.\n"); 37362306a36Sopenharmony_ci qla24xx_configure_vp(vha); 37462306a36Sopenharmony_ci ql_dbg(ql_dbg_dpc, vha, 0x4015, 37562306a36Sopenharmony_ci "Configure VP end.\n"); 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (test_bit(PROCESS_PUREX_IOCB, &vha->dpc_flags)) { 38162306a36Sopenharmony_ci if (atomic_read(&vha->loop_state) == LOOP_READY) { 38262306a36Sopenharmony_ci qla24xx_process_purex_list(&vha->purex_list); 38362306a36Sopenharmony_ci clear_bit(PROCESS_PUREX_IOCB, &vha->dpc_flags); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (test_bit(RELOGIN_NEEDED, &vha->dpc_flags) && 38862306a36Sopenharmony_ci !test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) && 38962306a36Sopenharmony_ci atomic_read(&vha->loop_state) != LOOP_DOWN) { 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (!vha->relogin_jif || 39262306a36Sopenharmony_ci time_after_eq(jiffies, vha->relogin_jif)) { 39362306a36Sopenharmony_ci vha->relogin_jif = jiffies + HZ; 39462306a36Sopenharmony_ci clear_bit(RELOGIN_NEEDED, &vha->dpc_flags); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ql_dbg(ql_dbg_dpc, vha, 0x4018, 39762306a36Sopenharmony_ci "Relogin needed scheduled.\n"); 39862306a36Sopenharmony_ci qla24xx_post_relogin_work(vha); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) && 40362306a36Sopenharmony_ci (!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) { 40462306a36Sopenharmony_ci clear_bit(RESET_ACTIVE, &vha->dpc_flags); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { 40862306a36Sopenharmony_ci if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) { 40962306a36Sopenharmony_ci ql_dbg(ql_dbg_dpc, vha, 0x401a, 41062306a36Sopenharmony_ci "Loop resync scheduled.\n"); 41162306a36Sopenharmony_ci qla2x00_loop_resync(vha); 41262306a36Sopenharmony_ci clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags); 41362306a36Sopenharmony_ci ql_dbg(ql_dbg_dpc, vha, 0x401b, 41462306a36Sopenharmony_ci "Loop resync end.\n"); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x401c, 41962306a36Sopenharmony_ci "Exiting %s.\n", __func__); 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_civoid 42462306a36Sopenharmony_ciqla2x00_do_dpc_all_vps(scsi_qla_host_t *vha) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 42762306a36Sopenharmony_ci scsi_qla_host_t *vp, *tvp; 42862306a36Sopenharmony_ci unsigned long flags = 0; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (vha->vp_idx) 43162306a36Sopenharmony_ci return; 43262306a36Sopenharmony_ci if (list_empty(&ha->vp_list)) 43362306a36Sopenharmony_ci return; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci clear_bit(VP_DPC_NEEDED, &vha->dpc_flags); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!(ha->current_topology & ISP_CFG_F)) 43862306a36Sopenharmony_ci return; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 44162306a36Sopenharmony_ci list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) { 44262306a36Sopenharmony_ci if (vp->vp_idx) { 44362306a36Sopenharmony_ci atomic_inc(&vp->vref_count); 44462306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci qla2x00_do_dpc_vp(vp); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci spin_lock_irqsave(&ha->vport_slock, flags); 44962306a36Sopenharmony_ci atomic_dec(&vp->vref_count); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci spin_unlock_irqrestore(&ha->vport_slock, flags); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciint 45662306a36Sopenharmony_ciqla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost); 45962306a36Sopenharmony_ci struct qla_hw_data *ha = base_vha->hw; 46062306a36Sopenharmony_ci scsi_qla_host_t *vha; 46162306a36Sopenharmony_ci uint8_t port_name[WWN_SIZE]; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR) 46462306a36Sopenharmony_ci return VPCERR_UNSUPPORTED; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Check up the F/W and H/W support NPIV */ 46762306a36Sopenharmony_ci if (!ha->flags.npiv_supported) 46862306a36Sopenharmony_ci return VPCERR_UNSUPPORTED; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Check up whether npiv supported switch presented */ 47162306a36Sopenharmony_ci if (!(ha->switch_cap & FLOGI_MID_SUPPORT)) 47262306a36Sopenharmony_ci return VPCERR_NO_FABRIC_SUPP; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Check up unique WWPN */ 47562306a36Sopenharmony_ci u64_to_wwn(fc_vport->port_name, port_name); 47662306a36Sopenharmony_ci if (!memcmp(port_name, base_vha->port_name, WWN_SIZE)) 47762306a36Sopenharmony_ci return VPCERR_BAD_WWN; 47862306a36Sopenharmony_ci vha = qla24xx_find_vhost_by_name(ha, port_name); 47962306a36Sopenharmony_ci if (vha) 48062306a36Sopenharmony_ci return VPCERR_BAD_WWN; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Check up max-npiv-supports */ 48362306a36Sopenharmony_ci if (ha->num_vhosts > ha->max_npiv_vports) { 48462306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xa004, 48562306a36Sopenharmony_ci "num_vhosts %ud is bigger " 48662306a36Sopenharmony_ci "than max_npiv_vports %ud.\n", 48762306a36Sopenharmony_ci ha->num_vhosts, ha->max_npiv_vports); 48862306a36Sopenharmony_ci return VPCERR_UNSUPPORTED; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ciscsi_qla_host_t * 49462306a36Sopenharmony_ciqla24xx_create_vhost(struct fc_vport *fc_vport) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost); 49762306a36Sopenharmony_ci struct qla_hw_data *ha = base_vha->hw; 49862306a36Sopenharmony_ci scsi_qla_host_t *vha; 49962306a36Sopenharmony_ci const struct scsi_host_template *sht = &qla2xxx_driver_template; 50062306a36Sopenharmony_ci struct Scsi_Host *host; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci vha = qla2x00_create_host(sht, ha); 50362306a36Sopenharmony_ci if (!vha) { 50462306a36Sopenharmony_ci ql_log(ql_log_warn, vha, 0xa005, 50562306a36Sopenharmony_ci "scsi_host_alloc() failed for vport.\n"); 50662306a36Sopenharmony_ci return(NULL); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci host = vha->host; 51062306a36Sopenharmony_ci fc_vport->dd_data = vha; 51162306a36Sopenharmony_ci /* New host info */ 51262306a36Sopenharmony_ci u64_to_wwn(fc_vport->node_name, vha->node_name); 51362306a36Sopenharmony_ci u64_to_wwn(fc_vport->port_name, vha->port_name); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci vha->fc_vport = fc_vport; 51662306a36Sopenharmony_ci vha->device_flags = 0; 51762306a36Sopenharmony_ci vha->vp_idx = qla24xx_allocate_vp_id(vha); 51862306a36Sopenharmony_ci if (vha->vp_idx > ha->max_npiv_vports) { 51962306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xa006, 52062306a36Sopenharmony_ci "Couldn't allocate vp_id.\n"); 52162306a36Sopenharmony_ci goto create_vhost_failed; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci vha->mgmt_svr_loop_id = qla2x00_reserve_mgmt_server_loop_id(vha); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci vha->dpc_flags = 0L; 52662306a36Sopenharmony_ci ha->dpc_active = 0; 52762306a36Sopenharmony_ci set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); 52862306a36Sopenharmony_ci set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * To fix the issue of processing a parent's RSCN for the vport before 53262306a36Sopenharmony_ci * its SCR is complete. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci set_bit(VP_SCR_NEEDED, &vha->vp_flags); 53562306a36Sopenharmony_ci atomic_set(&vha->loop_state, LOOP_DOWN); 53662306a36Sopenharmony_ci atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci qla2x00_start_timer(vha, WATCH_INTERVAL); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci vha->req = base_vha->req; 54162306a36Sopenharmony_ci vha->flags.nvme_enabled = base_vha->flags.nvme_enabled; 54262306a36Sopenharmony_ci host->can_queue = base_vha->req->length + 128; 54362306a36Sopenharmony_ci host->cmd_per_lun = 3; 54462306a36Sopenharmony_ci if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) 54562306a36Sopenharmony_ci host->max_cmd_len = 32; 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci host->max_cmd_len = MAX_CMDSZ; 54862306a36Sopenharmony_ci host->max_channel = MAX_BUSES - 1; 54962306a36Sopenharmony_ci host->max_lun = ql2xmaxlun; 55062306a36Sopenharmony_ci host->unique_id = host->host_no; 55162306a36Sopenharmony_ci host->max_id = ha->max_fibre_devices; 55262306a36Sopenharmony_ci host->transportt = qla2xxx_transport_vport_template; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xa007, 55562306a36Sopenharmony_ci "Detect vport hba %ld at address = %p.\n", 55662306a36Sopenharmony_ci vha->host_no, vha); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci vha->flags.init_done = 1; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci mutex_lock(&ha->vport_lock); 56162306a36Sopenharmony_ci set_bit(vha->vp_idx, ha->vp_idx_map); 56262306a36Sopenharmony_ci ha->cur_vport_count++; 56362306a36Sopenharmony_ci mutex_unlock(&ha->vport_lock); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return vha; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cicreate_vhost_failed: 56862306a36Sopenharmony_ci return NULL; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic void 57262306a36Sopenharmony_ciqla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 57562306a36Sopenharmony_ci uint16_t que_id = req->id; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci dma_free_coherent(&ha->pdev->dev, (req->length + 1) * 57862306a36Sopenharmony_ci sizeof(request_t), req->ring, req->dma); 57962306a36Sopenharmony_ci req->ring = NULL; 58062306a36Sopenharmony_ci req->dma = 0; 58162306a36Sopenharmony_ci if (que_id) { 58262306a36Sopenharmony_ci ha->req_q_map[que_id] = NULL; 58362306a36Sopenharmony_ci mutex_lock(&ha->vport_lock); 58462306a36Sopenharmony_ci clear_bit(que_id, ha->req_qid_map); 58562306a36Sopenharmony_ci mutex_unlock(&ha->vport_lock); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci kfree(req->outstanding_cmds); 58862306a36Sopenharmony_ci kfree(req); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void 59262306a36Sopenharmony_ciqla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 59562306a36Sopenharmony_ci uint16_t que_id = rsp->id; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (rsp->msix && rsp->msix->have_irq) { 59862306a36Sopenharmony_ci free_irq(rsp->msix->vector, rsp->msix->handle); 59962306a36Sopenharmony_ci rsp->msix->have_irq = 0; 60062306a36Sopenharmony_ci rsp->msix->in_use = 0; 60162306a36Sopenharmony_ci rsp->msix->handle = NULL; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) * 60462306a36Sopenharmony_ci sizeof(response_t), rsp->ring, rsp->dma); 60562306a36Sopenharmony_ci rsp->ring = NULL; 60662306a36Sopenharmony_ci rsp->dma = 0; 60762306a36Sopenharmony_ci if (que_id) { 60862306a36Sopenharmony_ci ha->rsp_q_map[que_id] = NULL; 60962306a36Sopenharmony_ci mutex_lock(&ha->vport_lock); 61062306a36Sopenharmony_ci clear_bit(que_id, ha->rsp_qid_map); 61162306a36Sopenharmony_ci mutex_unlock(&ha->vport_lock); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci kfree(rsp); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciint 61762306a36Sopenharmony_ciqla25xx_delete_req_que(struct scsi_qla_host *vha, struct req_que *req) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci int ret = QLA_SUCCESS; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (req && vha->flags.qpairs_req_created) { 62262306a36Sopenharmony_ci req->options |= BIT_0; 62362306a36Sopenharmony_ci ret = qla25xx_init_req_que(vha, req); 62462306a36Sopenharmony_ci if (ret != QLA_SUCCESS) 62562306a36Sopenharmony_ci return QLA_FUNCTION_FAILED; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci qla25xx_free_req_que(vha, req); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ciint 63462306a36Sopenharmony_ciqla25xx_delete_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci int ret = QLA_SUCCESS; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (rsp && vha->flags.qpairs_rsp_created) { 63962306a36Sopenharmony_ci rsp->options |= BIT_0; 64062306a36Sopenharmony_ci ret = qla25xx_init_rsp_que(vha, rsp); 64162306a36Sopenharmony_ci if (ret != QLA_SUCCESS) 64262306a36Sopenharmony_ci return QLA_FUNCTION_FAILED; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci qla25xx_free_rsp_que(vha, rsp); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* Delete all queues for a given vhost */ 65162306a36Sopenharmony_ciint 65262306a36Sopenharmony_ciqla25xx_delete_queues(struct scsi_qla_host *vha) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci int cnt, ret = 0; 65562306a36Sopenharmony_ci struct req_que *req = NULL; 65662306a36Sopenharmony_ci struct rsp_que *rsp = NULL; 65762306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 65862306a36Sopenharmony_ci struct qla_qpair *qpair, *tqpair; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (ql2xmqsupport || ql2xnvmeenable) { 66162306a36Sopenharmony_ci list_for_each_entry_safe(qpair, tqpair, &vha->qp_list, 66262306a36Sopenharmony_ci qp_list_elem) 66362306a36Sopenharmony_ci qla2xxx_delete_qpair(vha, qpair); 66462306a36Sopenharmony_ci } else { 66562306a36Sopenharmony_ci /* Delete request queues */ 66662306a36Sopenharmony_ci for (cnt = 1; cnt < ha->max_req_queues; cnt++) { 66762306a36Sopenharmony_ci req = ha->req_q_map[cnt]; 66862306a36Sopenharmony_ci if (req && test_bit(cnt, ha->req_qid_map)) { 66962306a36Sopenharmony_ci ret = qla25xx_delete_req_que(vha, req); 67062306a36Sopenharmony_ci if (ret != QLA_SUCCESS) { 67162306a36Sopenharmony_ci ql_log(ql_log_warn, vha, 0x00ea, 67262306a36Sopenharmony_ci "Couldn't delete req que %d.\n", 67362306a36Sopenharmony_ci req->id); 67462306a36Sopenharmony_ci return ret; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Delete response queues */ 68062306a36Sopenharmony_ci for (cnt = 1; cnt < ha->max_rsp_queues; cnt++) { 68162306a36Sopenharmony_ci rsp = ha->rsp_q_map[cnt]; 68262306a36Sopenharmony_ci if (rsp && test_bit(cnt, ha->rsp_qid_map)) { 68362306a36Sopenharmony_ci ret = qla25xx_delete_rsp_que(vha, rsp); 68462306a36Sopenharmony_ci if (ret != QLA_SUCCESS) { 68562306a36Sopenharmony_ci ql_log(ql_log_warn, vha, 0x00eb, 68662306a36Sopenharmony_ci "Couldn't delete rsp que %d.\n", 68762306a36Sopenharmony_ci rsp->id); 68862306a36Sopenharmony_ci return ret; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ciint 69862306a36Sopenharmony_ciqla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, 69962306a36Sopenharmony_ci uint8_t vp_idx, uint16_t rid, int rsp_que, uint8_t qos, bool startqp) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci int ret = 0; 70262306a36Sopenharmony_ci struct req_que *req = NULL; 70362306a36Sopenharmony_ci struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); 70462306a36Sopenharmony_ci struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev); 70562306a36Sopenharmony_ci uint16_t que_id = 0; 70662306a36Sopenharmony_ci device_reg_t *reg; 70762306a36Sopenharmony_ci uint32_t cnt; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci req = kzalloc(sizeof(struct req_que), GFP_KERNEL); 71062306a36Sopenharmony_ci if (req == NULL) { 71162306a36Sopenharmony_ci ql_log(ql_log_fatal, base_vha, 0x00d9, 71262306a36Sopenharmony_ci "Failed to allocate memory for request queue.\n"); 71362306a36Sopenharmony_ci goto failed; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci req->length = REQUEST_ENTRY_CNT_24XX; 71762306a36Sopenharmony_ci req->ring = dma_alloc_coherent(&ha->pdev->dev, 71862306a36Sopenharmony_ci (req->length + 1) * sizeof(request_t), 71962306a36Sopenharmony_ci &req->dma, GFP_KERNEL); 72062306a36Sopenharmony_ci if (req->ring == NULL) { 72162306a36Sopenharmony_ci ql_log(ql_log_fatal, base_vha, 0x00da, 72262306a36Sopenharmony_ci "Failed to allocate memory for request_ring.\n"); 72362306a36Sopenharmony_ci goto que_failed; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ret = qla2x00_alloc_outstanding_cmds(ha, req); 72762306a36Sopenharmony_ci if (ret != QLA_SUCCESS) 72862306a36Sopenharmony_ci goto que_failed; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci mutex_lock(&ha->mq_lock); 73162306a36Sopenharmony_ci que_id = find_first_zero_bit(ha->req_qid_map, ha->max_req_queues); 73262306a36Sopenharmony_ci if (que_id >= ha->max_req_queues) { 73362306a36Sopenharmony_ci mutex_unlock(&ha->mq_lock); 73462306a36Sopenharmony_ci ql_log(ql_log_warn, base_vha, 0x00db, 73562306a36Sopenharmony_ci "No resources to create additional request queue.\n"); 73662306a36Sopenharmony_ci goto que_failed; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci set_bit(que_id, ha->req_qid_map); 73962306a36Sopenharmony_ci ha->req_q_map[que_id] = req; 74062306a36Sopenharmony_ci req->rid = rid; 74162306a36Sopenharmony_ci req->vp_idx = vp_idx; 74262306a36Sopenharmony_ci req->qos = qos; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci ql_dbg(ql_dbg_multiq, base_vha, 0xc002, 74562306a36Sopenharmony_ci "queue_id=%d rid=%d vp_idx=%d qos=%d.\n", 74662306a36Sopenharmony_ci que_id, req->rid, req->vp_idx, req->qos); 74762306a36Sopenharmony_ci ql_dbg(ql_dbg_init, base_vha, 0x00dc, 74862306a36Sopenharmony_ci "queue_id=%d rid=%d vp_idx=%d qos=%d.\n", 74962306a36Sopenharmony_ci que_id, req->rid, req->vp_idx, req->qos); 75062306a36Sopenharmony_ci if (rsp_que < 0) 75162306a36Sopenharmony_ci req->rsp = NULL; 75262306a36Sopenharmony_ci else 75362306a36Sopenharmony_ci req->rsp = ha->rsp_q_map[rsp_que]; 75462306a36Sopenharmony_ci /* Use alternate PCI bus number */ 75562306a36Sopenharmony_ci if (MSB(req->rid)) 75662306a36Sopenharmony_ci options |= BIT_4; 75762306a36Sopenharmony_ci /* Use alternate PCI devfn */ 75862306a36Sopenharmony_ci if (LSB(req->rid)) 75962306a36Sopenharmony_ci options |= BIT_5; 76062306a36Sopenharmony_ci req->options = options; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ql_dbg(ql_dbg_multiq, base_vha, 0xc003, 76362306a36Sopenharmony_ci "options=0x%x.\n", req->options); 76462306a36Sopenharmony_ci ql_dbg(ql_dbg_init, base_vha, 0x00dd, 76562306a36Sopenharmony_ci "options=0x%x.\n", req->options); 76662306a36Sopenharmony_ci for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) 76762306a36Sopenharmony_ci req->outstanding_cmds[cnt] = NULL; 76862306a36Sopenharmony_ci req->current_outstanding_cmd = 1; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci req->ring_ptr = req->ring; 77162306a36Sopenharmony_ci req->ring_index = 0; 77262306a36Sopenharmony_ci req->cnt = req->length; 77362306a36Sopenharmony_ci req->id = que_id; 77462306a36Sopenharmony_ci reg = ISP_QUE_REG(ha, que_id); 77562306a36Sopenharmony_ci req->req_q_in = ®->isp25mq.req_q_in; 77662306a36Sopenharmony_ci req->req_q_out = ®->isp25mq.req_q_out; 77762306a36Sopenharmony_ci req->max_q_depth = ha->req_q_map[0]->max_q_depth; 77862306a36Sopenharmony_ci req->out_ptr = (uint16_t *)(req->ring + req->length); 77962306a36Sopenharmony_ci mutex_unlock(&ha->mq_lock); 78062306a36Sopenharmony_ci ql_dbg(ql_dbg_multiq, base_vha, 0xc004, 78162306a36Sopenharmony_ci "ring_ptr=%p ring_index=%d, " 78262306a36Sopenharmony_ci "cnt=%d id=%d max_q_depth=%d.\n", 78362306a36Sopenharmony_ci req->ring_ptr, req->ring_index, 78462306a36Sopenharmony_ci req->cnt, req->id, req->max_q_depth); 78562306a36Sopenharmony_ci ql_dbg(ql_dbg_init, base_vha, 0x00de, 78662306a36Sopenharmony_ci "ring_ptr=%p ring_index=%d, " 78762306a36Sopenharmony_ci "cnt=%d id=%d max_q_depth=%d.\n", 78862306a36Sopenharmony_ci req->ring_ptr, req->ring_index, req->cnt, 78962306a36Sopenharmony_ci req->id, req->max_q_depth); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (startqp) { 79262306a36Sopenharmony_ci ret = qla25xx_init_req_que(base_vha, req); 79362306a36Sopenharmony_ci if (ret != QLA_SUCCESS) { 79462306a36Sopenharmony_ci ql_log(ql_log_fatal, base_vha, 0x00df, 79562306a36Sopenharmony_ci "%s failed.\n", __func__); 79662306a36Sopenharmony_ci mutex_lock(&ha->mq_lock); 79762306a36Sopenharmony_ci clear_bit(que_id, ha->req_qid_map); 79862306a36Sopenharmony_ci mutex_unlock(&ha->mq_lock); 79962306a36Sopenharmony_ci goto que_failed; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci vha->flags.qpairs_req_created = 1; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return req->id; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cique_failed: 80762306a36Sopenharmony_ci qla25xx_free_req_que(base_vha, req); 80862306a36Sopenharmony_cifailed: 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic void qla_do_work(struct work_struct *work) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci unsigned long flags; 81562306a36Sopenharmony_ci struct qla_qpair *qpair = container_of(work, struct qla_qpair, q_work); 81662306a36Sopenharmony_ci struct scsi_qla_host *vha = qpair->vha; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci spin_lock_irqsave(&qpair->qp_lock, flags); 81962306a36Sopenharmony_ci qla24xx_process_response_queue(vha, qpair->rsp); 82062306a36Sopenharmony_ci spin_unlock_irqrestore(&qpair->qp_lock, flags); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci/* create response queue */ 82562306a36Sopenharmony_ciint 82662306a36Sopenharmony_ciqla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, 82762306a36Sopenharmony_ci uint8_t vp_idx, uint16_t rid, struct qla_qpair *qpair, bool startqp) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci int ret = 0; 83062306a36Sopenharmony_ci struct rsp_que *rsp = NULL; 83162306a36Sopenharmony_ci struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); 83262306a36Sopenharmony_ci struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev); 83362306a36Sopenharmony_ci uint16_t que_id = 0; 83462306a36Sopenharmony_ci device_reg_t *reg; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL); 83762306a36Sopenharmony_ci if (rsp == NULL) { 83862306a36Sopenharmony_ci ql_log(ql_log_warn, base_vha, 0x0066, 83962306a36Sopenharmony_ci "Failed to allocate memory for response queue.\n"); 84062306a36Sopenharmony_ci goto failed; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci rsp->length = RESPONSE_ENTRY_CNT_MQ; 84462306a36Sopenharmony_ci rsp->ring = dma_alloc_coherent(&ha->pdev->dev, 84562306a36Sopenharmony_ci (rsp->length + 1) * sizeof(response_t), 84662306a36Sopenharmony_ci &rsp->dma, GFP_KERNEL); 84762306a36Sopenharmony_ci if (rsp->ring == NULL) { 84862306a36Sopenharmony_ci ql_log(ql_log_warn, base_vha, 0x00e1, 84962306a36Sopenharmony_ci "Failed to allocate memory for response ring.\n"); 85062306a36Sopenharmony_ci goto que_failed; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci mutex_lock(&ha->mq_lock); 85462306a36Sopenharmony_ci que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues); 85562306a36Sopenharmony_ci if (que_id >= ha->max_rsp_queues) { 85662306a36Sopenharmony_ci mutex_unlock(&ha->mq_lock); 85762306a36Sopenharmony_ci ql_log(ql_log_warn, base_vha, 0x00e2, 85862306a36Sopenharmony_ci "No resources to create additional request queue.\n"); 85962306a36Sopenharmony_ci goto que_failed; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci set_bit(que_id, ha->rsp_qid_map); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci rsp->msix = qpair->msix; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci ha->rsp_q_map[que_id] = rsp; 86662306a36Sopenharmony_ci rsp->rid = rid; 86762306a36Sopenharmony_ci rsp->vp_idx = vp_idx; 86862306a36Sopenharmony_ci rsp->hw = ha; 86962306a36Sopenharmony_ci ql_dbg(ql_dbg_init, base_vha, 0x00e4, 87062306a36Sopenharmony_ci "rsp queue_id=%d rid=%d vp_idx=%d hw=%p.\n", 87162306a36Sopenharmony_ci que_id, rsp->rid, rsp->vp_idx, rsp->hw); 87262306a36Sopenharmony_ci /* Use alternate PCI bus number */ 87362306a36Sopenharmony_ci if (MSB(rsp->rid)) 87462306a36Sopenharmony_ci options |= BIT_4; 87562306a36Sopenharmony_ci /* Use alternate PCI devfn */ 87662306a36Sopenharmony_ci if (LSB(rsp->rid)) 87762306a36Sopenharmony_ci options |= BIT_5; 87862306a36Sopenharmony_ci /* Enable MSIX handshake mode on for uncapable adapters */ 87962306a36Sopenharmony_ci if (!IS_MSIX_NACK_CAPABLE(ha)) 88062306a36Sopenharmony_ci options |= BIT_6; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* Set option to indicate response queue creation */ 88362306a36Sopenharmony_ci options |= BIT_1; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci rsp->options = options; 88662306a36Sopenharmony_ci rsp->id = que_id; 88762306a36Sopenharmony_ci reg = ISP_QUE_REG(ha, que_id); 88862306a36Sopenharmony_ci rsp->rsp_q_in = ®->isp25mq.rsp_q_in; 88962306a36Sopenharmony_ci rsp->rsp_q_out = ®->isp25mq.rsp_q_out; 89062306a36Sopenharmony_ci rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length); 89162306a36Sopenharmony_ci mutex_unlock(&ha->mq_lock); 89262306a36Sopenharmony_ci ql_dbg(ql_dbg_multiq, base_vha, 0xc00b, 89362306a36Sopenharmony_ci "options=%x id=%d rsp_q_in=%p rsp_q_out=%p\n", 89462306a36Sopenharmony_ci rsp->options, rsp->id, rsp->rsp_q_in, 89562306a36Sopenharmony_ci rsp->rsp_q_out); 89662306a36Sopenharmony_ci ql_dbg(ql_dbg_init, base_vha, 0x00e5, 89762306a36Sopenharmony_ci "options=%x id=%d rsp_q_in=%p rsp_q_out=%p\n", 89862306a36Sopenharmony_ci rsp->options, rsp->id, rsp->rsp_q_in, 89962306a36Sopenharmony_ci rsp->rsp_q_out); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci ret = qla25xx_request_irq(ha, qpair, qpair->msix, 90262306a36Sopenharmony_ci ha->flags.disable_msix_handshake ? 90362306a36Sopenharmony_ci QLA_MSIX_QPAIR_MULTIQ_RSP_Q : QLA_MSIX_QPAIR_MULTIQ_RSP_Q_HS); 90462306a36Sopenharmony_ci if (ret) 90562306a36Sopenharmony_ci goto que_failed; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (startqp) { 90862306a36Sopenharmony_ci ret = qla25xx_init_rsp_que(base_vha, rsp); 90962306a36Sopenharmony_ci if (ret != QLA_SUCCESS) { 91062306a36Sopenharmony_ci ql_log(ql_log_fatal, base_vha, 0x00e7, 91162306a36Sopenharmony_ci "%s failed.\n", __func__); 91262306a36Sopenharmony_ci mutex_lock(&ha->mq_lock); 91362306a36Sopenharmony_ci clear_bit(que_id, ha->rsp_qid_map); 91462306a36Sopenharmony_ci mutex_unlock(&ha->mq_lock); 91562306a36Sopenharmony_ci goto que_failed; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci vha->flags.qpairs_rsp_created = 1; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci rsp->req = NULL; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci qla2x00_init_response_q_entries(rsp); 92262306a36Sopenharmony_ci if (qpair->hw->wq) 92362306a36Sopenharmony_ci INIT_WORK(&qpair->q_work, qla_do_work); 92462306a36Sopenharmony_ci return rsp->id; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cique_failed: 92762306a36Sopenharmony_ci qla25xx_free_rsp_que(base_vha, rsp); 92862306a36Sopenharmony_cifailed: 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void qla_ctrlvp_sp_done(srb_t *sp, int res) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci if (sp->comp) 93562306a36Sopenharmony_ci complete(sp->comp); 93662306a36Sopenharmony_ci /* don't free sp here. Let the caller do the free */ 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/** 94062306a36Sopenharmony_ci * qla24xx_control_vp() - Enable a virtual port for given host 94162306a36Sopenharmony_ci * @vha: adapter block pointer 94262306a36Sopenharmony_ci * @cmd: command type to be sent for enable virtual port 94362306a36Sopenharmony_ci * 94462306a36Sopenharmony_ci * Return: qla2xxx local function return status code. 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_ciint qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci int rval = QLA_MEMORY_ALLOC_FAILED; 94962306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 95062306a36Sopenharmony_ci int vp_index = vha->vp_idx; 95162306a36Sopenharmony_ci struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); 95262306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(comp); 95362306a36Sopenharmony_ci srb_t *sp; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0x10c1, 95662306a36Sopenharmony_ci "Entered %s cmd %x index %d.\n", __func__, cmd, vp_index); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (vp_index == 0 || vp_index >= ha->max_npiv_vports) 95962306a36Sopenharmony_ci return QLA_PARAMETER_ERROR; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* ref: INIT */ 96262306a36Sopenharmony_ci sp = qla2x00_get_sp(base_vha, NULL, GFP_KERNEL); 96362306a36Sopenharmony_ci if (!sp) 96462306a36Sopenharmony_ci return rval; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci sp->type = SRB_CTRL_VP; 96762306a36Sopenharmony_ci sp->name = "ctrl_vp"; 96862306a36Sopenharmony_ci sp->comp = ∁ 96962306a36Sopenharmony_ci qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, 97062306a36Sopenharmony_ci qla_ctrlvp_sp_done); 97162306a36Sopenharmony_ci sp->u.iocb_cmd.u.ctrlvp.cmd = cmd; 97262306a36Sopenharmony_ci sp->u.iocb_cmd.u.ctrlvp.vp_index = vp_index; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci rval = qla2x00_start_sp(sp); 97562306a36Sopenharmony_ci if (rval != QLA_SUCCESS) { 97662306a36Sopenharmony_ci ql_dbg(ql_dbg_async, vha, 0xffff, 97762306a36Sopenharmony_ci "%s: %s Failed submission. %x.\n", 97862306a36Sopenharmony_ci __func__, sp->name, rval); 97962306a36Sopenharmony_ci goto done; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0x113f, "%s hndl %x submitted\n", 98362306a36Sopenharmony_ci sp->name, sp->handle); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci wait_for_completion(&comp); 98662306a36Sopenharmony_ci sp->comp = NULL; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci rval = sp->rc; 98962306a36Sopenharmony_ci switch (rval) { 99062306a36Sopenharmony_ci case QLA_FUNCTION_TIMEOUT: 99162306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xffff, "%s: %s Timeout. %x.\n", 99262306a36Sopenharmony_ci __func__, sp->name, rval); 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci case QLA_SUCCESS: 99562306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xffff, "%s: %s done.\n", 99662306a36Sopenharmony_ci __func__, sp->name); 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci default: 99962306a36Sopenharmony_ci ql_dbg(ql_dbg_vport, vha, 0xffff, "%s: %s Failed. %x.\n", 100062306a36Sopenharmony_ci __func__, sp->name, rval); 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_cidone: 100462306a36Sopenharmony_ci /* ref: INIT */ 100562306a36Sopenharmony_ci kref_put(&sp->cmd_kref, qla2x00_sp_release); 100662306a36Sopenharmony_ci return rval; 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistruct scsi_qla_host *qla_find_host_by_vp_idx(struct scsi_qla_host *vha, uint16_t vp_idx) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (vha->vp_idx == vp_idx) 101462306a36Sopenharmony_ci return vha; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci BUG_ON(ha->vp_map == NULL); 101762306a36Sopenharmony_ci if (likely(test_bit(vp_idx, ha->vp_idx_map))) 101862306a36Sopenharmony_ci return ha->vp_map[vp_idx].vha; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return NULL; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci/* vport_slock to be held by the caller */ 102462306a36Sopenharmony_civoid 102562306a36Sopenharmony_ciqla_update_vp_map(struct scsi_qla_host *vha, int cmd) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci void *slot; 102862306a36Sopenharmony_ci u32 key; 102962306a36Sopenharmony_ci int rc; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (!vha->hw->vp_map) 103262306a36Sopenharmony_ci return; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci key = vha->d_id.b24; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci switch (cmd) { 103762306a36Sopenharmony_ci case SET_VP_IDX: 103862306a36Sopenharmony_ci vha->hw->vp_map[vha->vp_idx].vha = vha; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci case SET_AL_PA: 104162306a36Sopenharmony_ci slot = btree_lookup32(&vha->hw->host_map, key); 104262306a36Sopenharmony_ci if (!slot) { 104362306a36Sopenharmony_ci ql_dbg(ql_dbg_disc, vha, 0xf018, 104462306a36Sopenharmony_ci "Save vha in host_map %p %06x\n", vha, key); 104562306a36Sopenharmony_ci rc = btree_insert32(&vha->hw->host_map, 104662306a36Sopenharmony_ci key, vha, GFP_ATOMIC); 104762306a36Sopenharmony_ci if (rc) 104862306a36Sopenharmony_ci ql_log(ql_log_info, vha, 0xd03e, 104962306a36Sopenharmony_ci "Unable to insert s_id into host_map: %06x\n", 105062306a36Sopenharmony_ci key); 105162306a36Sopenharmony_ci return; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci ql_dbg(ql_dbg_disc, vha, 0xf019, 105462306a36Sopenharmony_ci "replace existing vha in host_map %p %06x\n", vha, key); 105562306a36Sopenharmony_ci btree_update32(&vha->hw->host_map, key, vha); 105662306a36Sopenharmony_ci break; 105762306a36Sopenharmony_ci case RESET_VP_IDX: 105862306a36Sopenharmony_ci vha->hw->vp_map[vha->vp_idx].vha = NULL; 105962306a36Sopenharmony_ci break; 106062306a36Sopenharmony_ci case RESET_AL_PA: 106162306a36Sopenharmony_ci ql_dbg(ql_dbg_disc, vha, 0xf01a, 106262306a36Sopenharmony_ci "clear vha in host_map %p %06x\n", vha, key); 106362306a36Sopenharmony_ci slot = btree_lookup32(&vha->hw->host_map, key); 106462306a36Sopenharmony_ci if (slot) 106562306a36Sopenharmony_ci btree_remove32(&vha->hw->host_map, key); 106662306a36Sopenharmony_ci vha->d_id.b24 = 0; 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_civoid qla_update_host_map(struct scsi_qla_host *vha, port_id_t id) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (!vha->d_id.b24) { 107562306a36Sopenharmony_ci vha->d_id = id; 107662306a36Sopenharmony_ci qla_update_vp_map(vha, SET_AL_PA); 107762306a36Sopenharmony_ci } else if (vha->d_id.b24 != id.b24) { 107862306a36Sopenharmony_ci qla_update_vp_map(vha, RESET_AL_PA); 107962306a36Sopenharmony_ci vha->d_id = id; 108062306a36Sopenharmony_ci qla_update_vp_map(vha, SET_AL_PA); 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ciint qla_create_buf_pool(struct scsi_qla_host *vha, struct qla_qpair *qp) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci int sz; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci qp->buf_pool.num_bufs = qp->req->length; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci sz = BITS_TO_LONGS(qp->req->length); 109162306a36Sopenharmony_ci qp->buf_pool.buf_map = kcalloc(sz, sizeof(long), GFP_KERNEL); 109262306a36Sopenharmony_ci if (!qp->buf_pool.buf_map) { 109362306a36Sopenharmony_ci ql_log(ql_log_warn, vha, 0x0186, 109462306a36Sopenharmony_ci "Failed to allocate buf_map(%zd).\n", sz * sizeof(unsigned long)); 109562306a36Sopenharmony_ci return -ENOMEM; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci sz = qp->req->length * sizeof(void *); 109862306a36Sopenharmony_ci qp->buf_pool.buf_array = kcalloc(qp->req->length, sizeof(void *), GFP_KERNEL); 109962306a36Sopenharmony_ci if (!qp->buf_pool.buf_array) { 110062306a36Sopenharmony_ci ql_log(ql_log_warn, vha, 0x0186, 110162306a36Sopenharmony_ci "Failed to allocate buf_array(%d).\n", sz); 110262306a36Sopenharmony_ci kfree(qp->buf_pool.buf_map); 110362306a36Sopenharmony_ci return -ENOMEM; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci sz = qp->req->length * sizeof(dma_addr_t); 110662306a36Sopenharmony_ci qp->buf_pool.dma_array = kcalloc(qp->req->length, sizeof(dma_addr_t), GFP_KERNEL); 110762306a36Sopenharmony_ci if (!qp->buf_pool.dma_array) { 110862306a36Sopenharmony_ci ql_log(ql_log_warn, vha, 0x0186, 110962306a36Sopenharmony_ci "Failed to allocate dma_array(%d).\n", sz); 111062306a36Sopenharmony_ci kfree(qp->buf_pool.buf_map); 111162306a36Sopenharmony_ci kfree(qp->buf_pool.buf_array); 111262306a36Sopenharmony_ci return -ENOMEM; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci set_bit(0, qp->buf_pool.buf_map); 111562306a36Sopenharmony_ci return 0; 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_civoid qla_free_buf_pool(struct qla_qpair *qp) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci int i; 112162306a36Sopenharmony_ci struct qla_hw_data *ha = qp->vha->hw; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci for (i = 0; i < qp->buf_pool.num_bufs; i++) { 112462306a36Sopenharmony_ci if (qp->buf_pool.buf_array[i] && qp->buf_pool.dma_array[i]) 112562306a36Sopenharmony_ci dma_pool_free(ha->fcp_cmnd_dma_pool, qp->buf_pool.buf_array[i], 112662306a36Sopenharmony_ci qp->buf_pool.dma_array[i]); 112762306a36Sopenharmony_ci qp->buf_pool.buf_array[i] = NULL; 112862306a36Sopenharmony_ci qp->buf_pool.dma_array[i] = 0; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci kfree(qp->buf_pool.dma_array); 113262306a36Sopenharmony_ci kfree(qp->buf_pool.buf_array); 113362306a36Sopenharmony_ci kfree(qp->buf_pool.buf_map); 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci/* it is assume qp->qp_lock is held at this point */ 113762306a36Sopenharmony_ciint qla_get_buf(struct scsi_qla_host *vha, struct qla_qpair *qp, struct qla_buf_dsc *dsc) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci u16 tag, i = 0; 114062306a36Sopenharmony_ci void *buf; 114162306a36Sopenharmony_ci dma_addr_t buf_dma; 114262306a36Sopenharmony_ci struct qla_hw_data *ha = vha->hw; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci dsc->tag = TAG_FREED; 114562306a36Sopenharmony_ciagain: 114662306a36Sopenharmony_ci tag = find_first_zero_bit(qp->buf_pool.buf_map, qp->buf_pool.num_bufs); 114762306a36Sopenharmony_ci if (tag >= qp->buf_pool.num_bufs) { 114862306a36Sopenharmony_ci ql_dbg(ql_dbg_io, vha, 0x00e2, 114962306a36Sopenharmony_ci "qp(%d) ran out of buf resource.\n", qp->id); 115062306a36Sopenharmony_ci return -EIO; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci if (tag == 0) { 115362306a36Sopenharmony_ci set_bit(0, qp->buf_pool.buf_map); 115462306a36Sopenharmony_ci i++; 115562306a36Sopenharmony_ci if (i == 5) { 115662306a36Sopenharmony_ci ql_dbg(ql_dbg_io, vha, 0x00e3, 115762306a36Sopenharmony_ci "qp(%d) unable to get tag.\n", qp->id); 115862306a36Sopenharmony_ci return -EIO; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci goto again; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (!qp->buf_pool.buf_array[tag]) { 116462306a36Sopenharmony_ci buf = dma_pool_zalloc(ha->fcp_cmnd_dma_pool, GFP_ATOMIC, &buf_dma); 116562306a36Sopenharmony_ci if (!buf) { 116662306a36Sopenharmony_ci ql_log(ql_log_fatal, vha, 0x13b1, 116762306a36Sopenharmony_ci "Failed to allocate buf.\n"); 116862306a36Sopenharmony_ci return -ENOMEM; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci dsc->buf = qp->buf_pool.buf_array[tag] = buf; 117262306a36Sopenharmony_ci dsc->buf_dma = qp->buf_pool.dma_array[tag] = buf_dma; 117362306a36Sopenharmony_ci qp->buf_pool.num_alloc++; 117462306a36Sopenharmony_ci } else { 117562306a36Sopenharmony_ci dsc->buf = qp->buf_pool.buf_array[tag]; 117662306a36Sopenharmony_ci dsc->buf_dma = qp->buf_pool.dma_array[tag]; 117762306a36Sopenharmony_ci memset(dsc->buf, 0, FCP_CMND_DMA_POOL_SIZE); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci qp->buf_pool.num_active++; 118162306a36Sopenharmony_ci if (qp->buf_pool.num_active > qp->buf_pool.max_used) 118262306a36Sopenharmony_ci qp->buf_pool.max_used = qp->buf_pool.num_active; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci dsc->tag = tag; 118562306a36Sopenharmony_ci set_bit(tag, qp->buf_pool.buf_map); 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic void qla_trim_buf(struct qla_qpair *qp, u16 trim) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci int i, j; 119262306a36Sopenharmony_ci struct qla_hw_data *ha = qp->vha->hw; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (!trim) 119562306a36Sopenharmony_ci return; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci for (i = 0; i < trim; i++) { 119862306a36Sopenharmony_ci j = qp->buf_pool.num_alloc - 1; 119962306a36Sopenharmony_ci if (test_bit(j, qp->buf_pool.buf_map)) { 120062306a36Sopenharmony_ci ql_dbg(ql_dbg_io + ql_dbg_verbose, qp->vha, 0x300b, 120162306a36Sopenharmony_ci "QP id(%d): trim active buf[%d]. Remain %d bufs\n", 120262306a36Sopenharmony_ci qp->id, j, qp->buf_pool.num_alloc); 120362306a36Sopenharmony_ci return; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (qp->buf_pool.buf_array[j]) { 120762306a36Sopenharmony_ci dma_pool_free(ha->fcp_cmnd_dma_pool, qp->buf_pool.buf_array[j], 120862306a36Sopenharmony_ci qp->buf_pool.dma_array[j]); 120962306a36Sopenharmony_ci qp->buf_pool.buf_array[j] = NULL; 121062306a36Sopenharmony_ci qp->buf_pool.dma_array[j] = 0; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci qp->buf_pool.num_alloc--; 121362306a36Sopenharmony_ci if (!qp->buf_pool.num_alloc) 121462306a36Sopenharmony_ci break; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci ql_dbg(ql_dbg_io + ql_dbg_verbose, qp->vha, 0x3010, 121762306a36Sopenharmony_ci "QP id(%d): trimmed %d bufs. Remain %d bufs\n", 121862306a36Sopenharmony_ci qp->id, trim, qp->buf_pool.num_alloc); 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic void __qla_adjust_buf(struct qla_qpair *qp) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci u32 trim; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci qp->buf_pool.take_snapshot = 0; 122662306a36Sopenharmony_ci qp->buf_pool.prev_max = qp->buf_pool.max_used; 122762306a36Sopenharmony_ci qp->buf_pool.max_used = qp->buf_pool.num_active; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci if (qp->buf_pool.prev_max > qp->buf_pool.max_used && 123062306a36Sopenharmony_ci qp->buf_pool.num_alloc > qp->buf_pool.max_used) { 123162306a36Sopenharmony_ci /* down trend */ 123262306a36Sopenharmony_ci trim = qp->buf_pool.num_alloc - qp->buf_pool.max_used; 123362306a36Sopenharmony_ci trim = (trim * 10) / 100; 123462306a36Sopenharmony_ci trim = trim ? trim : 1; 123562306a36Sopenharmony_ci qla_trim_buf(qp, trim); 123662306a36Sopenharmony_ci } else if (!qp->buf_pool.prev_max && !qp->buf_pool.max_used) { 123762306a36Sopenharmony_ci /* 2 periods of no io */ 123862306a36Sopenharmony_ci qla_trim_buf(qp, qp->buf_pool.num_alloc); 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci/* it is assume qp->qp_lock is held at this point */ 124362306a36Sopenharmony_civoid qla_put_buf(struct qla_qpair *qp, struct qla_buf_dsc *dsc) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci if (dsc->tag == TAG_FREED) 124662306a36Sopenharmony_ci return; 124762306a36Sopenharmony_ci lockdep_assert_held(qp->qp_lock_ptr); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci clear_bit(dsc->tag, qp->buf_pool.buf_map); 125062306a36Sopenharmony_ci qp->buf_pool.num_active--; 125162306a36Sopenharmony_ci dsc->tag = TAG_FREED; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci if (qp->buf_pool.take_snapshot) 125462306a36Sopenharmony_ci __qla_adjust_buf(qp); 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci#define EXPIRE (60 * HZ) 125862306a36Sopenharmony_civoid qla_adjust_buf(struct scsi_qla_host *vha) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci unsigned long flags; 126162306a36Sopenharmony_ci int i; 126262306a36Sopenharmony_ci struct qla_qpair *qp; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (vha->vp_idx) 126562306a36Sopenharmony_ci return; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (!vha->buf_expired) { 126862306a36Sopenharmony_ci vha->buf_expired = jiffies + EXPIRE; 126962306a36Sopenharmony_ci return; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci if (time_before(jiffies, vha->buf_expired)) 127262306a36Sopenharmony_ci return; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci vha->buf_expired = jiffies + EXPIRE; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci for (i = 0; i < vha->hw->num_qpairs; i++) { 127762306a36Sopenharmony_ci qp = vha->hw->queue_pair_map[i]; 127862306a36Sopenharmony_ci if (!qp) 127962306a36Sopenharmony_ci continue; 128062306a36Sopenharmony_ci if (!qp->buf_pool.num_alloc) 128162306a36Sopenharmony_ci continue; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci if (qp->buf_pool.take_snapshot) { 128462306a36Sopenharmony_ci /* no io has gone through in the last EXPIRE period */ 128562306a36Sopenharmony_ci spin_lock_irqsave(qp->qp_lock_ptr, flags); 128662306a36Sopenharmony_ci __qla_adjust_buf(qp); 128762306a36Sopenharmony_ci spin_unlock_irqrestore(qp->qp_lock_ptr, flags); 128862306a36Sopenharmony_ci } else { 128962306a36Sopenharmony_ci qp->buf_pool.take_snapshot = 1; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci} 1293