linux/drivers/net/ethernet/qlogic/qed/qed_fcoe.c

1033 lines
29 KiB
C
Raw Normal View History

/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and /or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/types.h>
#include <asm/byteorder.h>
#include <asm/param.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/version.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#define __PREVENT_DUMP_MEM_ARR__
#define __PREVENT_PXP_GLOBAL_WIN__
#include "qed.h"
#include "qed_cxt.h"
#include "qed_dev_api.h"
#include "qed_fcoe.h"
#include "qed_hsi.h"
#include "qed_hw.h"
#include "qed_int.h"
#include "qed_ll2.h"
#include "qed_mcp.h"
#include "qed_reg_addr.h"
#include "qed_sp.h"
#include "qed_sriov.h"
#include <linux/qed/qed_fcoe_if.h>
struct qed_fcoe_conn {
struct list_head list_entry;
bool free_on_delete;
u16 conn_id;
u32 icid;
u32 fw_cid;
u8 layer_code;
dma_addr_t sq_pbl_addr;
dma_addr_t sq_curr_page_addr;
dma_addr_t sq_next_page_addr;
dma_addr_t xferq_pbl_addr;
void *xferq_pbl_addr_virt_addr;
dma_addr_t xferq_addr[4];
void *xferq_addr_virt_addr[4];
dma_addr_t confq_pbl_addr;
void *confq_pbl_addr_virt_addr;
dma_addr_t confq_addr[2];
void *confq_addr_virt_addr[2];
dma_addr_t terminate_params;
u16 dst_mac_addr_lo;
u16 dst_mac_addr_mid;
u16 dst_mac_addr_hi;
u16 src_mac_addr_lo;
u16 src_mac_addr_mid;
u16 src_mac_addr_hi;
u16 tx_max_fc_pay_len;
u16 e_d_tov_timer_val;
u16 rec_tov_timer_val;
u16 rx_max_fc_pay_len;
u16 vlan_tag;
u16 physical_q0;
struct fc_addr_nw s_id;
u8 max_conc_seqs_c3;
struct fc_addr_nw d_id;
u8 flags;
u8 def_q_idx;
};
static int
qed_sp_fcoe_func_start(struct qed_hwfn *p_hwfn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct qed_fcoe_pf_params *fcoe_pf_params = NULL;
struct fcoe_init_ramrod_params *p_ramrod = NULL;
struct fcoe_init_func_ramrod_data *p_data;
struct fcoe_conn_context *p_cxt = NULL;
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
struct qed_cxt_info cxt_info;
u32 dummy_cid;
int rc = 0;
u16 tmp;
u8 i;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
FCOE_RAMROD_CMD_ID_INIT_FUNC,
PROTOCOLID_FCOE, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.fcoe_init;
p_data = &p_ramrod->init_ramrod_data;
fcoe_pf_params = &p_hwfn->pf_params.fcoe_pf_params;
p_data->mtu = cpu_to_le16(fcoe_pf_params->mtu);
tmp = cpu_to_le16(fcoe_pf_params->sq_num_pbl_pages);
p_data->sq_num_pages_in_pbl = tmp;
rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_FCOE, &dummy_cid);
if (rc)
return rc;
cxt_info.iid = dummy_cid;
rc = qed_cxt_get_cid_info(p_hwfn, &cxt_info);
if (rc) {
DP_NOTICE(p_hwfn, "Cannot find context info for dummy cid=%d\n",
dummy_cid);
return rc;
}
p_cxt = cxt_info.p_cxt;
SET_FIELD(p_cxt->tstorm_ag_context.flags3,
TSTORM_FCOE_CONN_AG_CTX_DUMMY_TIMER_CF_EN, 1);
fcoe_pf_params->dummy_icid = (u16)dummy_cid;
tmp = cpu_to_le16(fcoe_pf_params->num_tasks);
p_data->func_params.num_tasks = tmp;
p_data->func_params.log_page_size = fcoe_pf_params->log_page_size;
p_data->func_params.debug_mode = fcoe_pf_params->debug_mode;
DMA_REGPAIR_LE(p_data->q_params.glbl_q_params_addr,
fcoe_pf_params->glbl_q_params_addr);
tmp = cpu_to_le16(fcoe_pf_params->cq_num_entries);
p_data->q_params.cq_num_entries = tmp;
tmp = cpu_to_le16(fcoe_pf_params->cmdq_num_entries);
p_data->q_params.cmdq_num_entries = tmp;
tmp = fcoe_pf_params->num_cqs;
p_data->q_params.num_queues = (u8)tmp;
tmp = (u16)p_hwfn->hw_info.resc_start[QED_CMDQS_CQS];
p_data->q_params.queue_relative_offset = (u8)tmp;
for (i = 0; i < fcoe_pf_params->num_cqs; i++) {
tmp = cpu_to_le16(p_hwfn->sbs_info[i]->igu_sb_id);
p_data->q_params.cq_cmdq_sb_num_arr[i] = tmp;
}
p_data->q_params.cq_sb_pi = fcoe_pf_params->gl_rq_pi;
p_data->q_params.cmdq_sb_pi = fcoe_pf_params->gl_cmd_pi;
p_data->q_params.bdq_resource_id = (u8)RESC_START(p_hwfn, QED_BDQ);
DMA_REGPAIR_LE(p_data->q_params.bdq_pbl_base_address[BDQ_ID_RQ],
fcoe_pf_params->bdq_pbl_base_addr[BDQ_ID_RQ]);
p_data->q_params.bdq_pbl_num_entries[BDQ_ID_RQ] =
fcoe_pf_params->bdq_pbl_num_entries[BDQ_ID_RQ];
tmp = fcoe_pf_params->bdq_xoff_threshold[BDQ_ID_RQ];
p_data->q_params.bdq_xoff_threshold[BDQ_ID_RQ] = cpu_to_le16(tmp);
tmp = fcoe_pf_params->bdq_xon_threshold[BDQ_ID_RQ];
p_data->q_params.bdq_xon_threshold[BDQ_ID_RQ] = cpu_to_le16(tmp);
DMA_REGPAIR_LE(p_data->q_params.bdq_pbl_base_address[BDQ_ID_IMM_DATA],
fcoe_pf_params->bdq_pbl_base_addr[BDQ_ID_IMM_DATA]);
p_data->q_params.bdq_pbl_num_entries[BDQ_ID_IMM_DATA] =
fcoe_pf_params->bdq_pbl_num_entries[BDQ_ID_IMM_DATA];
tmp = fcoe_pf_params->bdq_xoff_threshold[BDQ_ID_IMM_DATA];
p_data->q_params.bdq_xoff_threshold[BDQ_ID_IMM_DATA] = cpu_to_le16(tmp);
tmp = fcoe_pf_params->bdq_xon_threshold[BDQ_ID_IMM_DATA];
p_data->q_params.bdq_xon_threshold[BDQ_ID_IMM_DATA] = cpu_to_le16(tmp);
tmp = fcoe_pf_params->rq_buffer_size;
p_data->q_params.rq_buffer_size = cpu_to_le16(tmp);
if (fcoe_pf_params->is_target) {
SET_FIELD(p_data->q_params.q_validity,
SCSI_INIT_FUNC_QUEUES_RQ_VALID, 1);
if (p_data->q_params.bdq_pbl_num_entries[BDQ_ID_IMM_DATA])
SET_FIELD(p_data->q_params.q_validity,
SCSI_INIT_FUNC_QUEUES_IMM_DATA_VALID, 1);
SET_FIELD(p_data->q_params.q_validity,
SCSI_INIT_FUNC_QUEUES_CMD_VALID, 1);
} else {
SET_FIELD(p_data->q_params.q_validity,
SCSI_INIT_FUNC_QUEUES_RQ_VALID, 1);
}
rc = qed_spq_post(p_hwfn, p_ent, NULL);
return rc;
}
static int
qed_sp_fcoe_conn_offload(struct qed_hwfn *p_hwfn,
struct qed_fcoe_conn *p_conn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct fcoe_conn_offload_ramrod_params *p_ramrod = NULL;
struct fcoe_conn_offload_ramrod_data *p_data;
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
u16 physical_q0, tmp;
int rc;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = p_conn->icid;
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
FCOE_RAMROD_CMD_ID_OFFLOAD_CONN,
PROTOCOLID_FCOE, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.fcoe_conn_ofld;
p_data = &p_ramrod->offload_ramrod_data;
/* Transmission PQ is the first of the PF */
physical_q0 = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD);
p_conn->physical_q0 = cpu_to_le16(physical_q0);
p_data->physical_q0 = cpu_to_le16(physical_q0);
p_data->conn_id = cpu_to_le16(p_conn->conn_id);
DMA_REGPAIR_LE(p_data->sq_pbl_addr, p_conn->sq_pbl_addr);
DMA_REGPAIR_LE(p_data->sq_curr_page_addr, p_conn->sq_curr_page_addr);
DMA_REGPAIR_LE(p_data->sq_next_page_addr, p_conn->sq_next_page_addr);
DMA_REGPAIR_LE(p_data->xferq_pbl_addr, p_conn->xferq_pbl_addr);
DMA_REGPAIR_LE(p_data->xferq_curr_page_addr, p_conn->xferq_addr[0]);
DMA_REGPAIR_LE(p_data->xferq_next_page_addr, p_conn->xferq_addr[1]);
DMA_REGPAIR_LE(p_data->respq_pbl_addr, p_conn->confq_pbl_addr);
DMA_REGPAIR_LE(p_data->respq_curr_page_addr, p_conn->confq_addr[0]);
DMA_REGPAIR_LE(p_data->respq_next_page_addr, p_conn->confq_addr[1]);
p_data->dst_mac_addr_lo = cpu_to_le16(p_conn->dst_mac_addr_lo);
p_data->dst_mac_addr_mid = cpu_to_le16(p_conn->dst_mac_addr_mid);
p_data->dst_mac_addr_hi = cpu_to_le16(p_conn->dst_mac_addr_hi);
p_data->src_mac_addr_lo = cpu_to_le16(p_conn->src_mac_addr_lo);
p_data->src_mac_addr_mid = cpu_to_le16(p_conn->src_mac_addr_mid);
p_data->src_mac_addr_hi = cpu_to_le16(p_conn->src_mac_addr_hi);
tmp = cpu_to_le16(p_conn->tx_max_fc_pay_len);
p_data->tx_max_fc_pay_len = tmp;
tmp = cpu_to_le16(p_conn->e_d_tov_timer_val);
p_data->e_d_tov_timer_val = tmp;
tmp = cpu_to_le16(p_conn->rec_tov_timer_val);
p_data->rec_rr_tov_timer_val = tmp;
tmp = cpu_to_le16(p_conn->rx_max_fc_pay_len);
p_data->rx_max_fc_pay_len = tmp;
p_data->vlan_tag = cpu_to_le16(p_conn->vlan_tag);
p_data->s_id.addr_hi = p_conn->s_id.addr_hi;
p_data->s_id.addr_mid = p_conn->s_id.addr_mid;
p_data->s_id.addr_lo = p_conn->s_id.addr_lo;
p_data->max_conc_seqs_c3 = p_conn->max_conc_seqs_c3;
p_data->d_id.addr_hi = p_conn->d_id.addr_hi;
p_data->d_id.addr_mid = p_conn->d_id.addr_mid;
p_data->d_id.addr_lo = p_conn->d_id.addr_lo;
p_data->flags = p_conn->flags;
p_data->def_q_idx = p_conn->def_q_idx;
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static int
qed_sp_fcoe_conn_destroy(struct qed_hwfn *p_hwfn,
struct qed_fcoe_conn *p_conn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct fcoe_conn_terminate_ramrod_params *p_ramrod = NULL;
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc = 0;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = p_conn->icid;
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
FCOE_RAMROD_CMD_ID_TERMINATE_CONN,
PROTOCOLID_FCOE, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.fcoe_conn_terminate;
DMA_REGPAIR_LE(p_ramrod->terminate_ramrod_data.terminate_params_addr,
p_conn->terminate_params);
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static int
qed_sp_fcoe_func_stop(struct qed_hwfn *p_hwfn,
qed: Don't use main-ptt in unrelated flows In order to access HW registers driver needs to acquire a PTT entry [mapping between bar memory and internal chip address]. Since acquiring PTT entries could fail [at least in theory] as their number is finite and other flows can hold them, we reserve special PTT entries for 'important' enough flows - ones we want to guarantee that would not be susceptible to such issues. One such special entry is the 'main' PTT which is meant to be used in flows such as chip initialization and de-initialization. However, there are other flows that are also using that same entry for their own purpose, and might run concurrently with the original flows [notice that for most cases using the main-ptt by mistake, such a race is still impossible, at least today]. This patch re-organizes the various functions that currently use the main_ptt in one of two ways: - If a function shouldn't use the main_ptt it starts acquiring and releasing it's own PTT entry and use it instead. Notice if those functions previously couldn't fail, they now can [as acquisition might fail]. - Change the prototypes so that the main_ptt would be received as a parameter [instead of explicitly accessing it]. This prevents the future risk of adding codes that introduces new use-cases for flows using the main_ptt, ones that might be in race with the actual 'main' flows. Signed-off-by: Rahul Verma <Rahul.Verma@cavium.com> Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2017-04-06 20:58:29 +08:00
struct qed_ptt *p_ptt,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
u32 active_segs = 0;
int rc = 0;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = p_hwfn->pf_params.fcoe_pf_params.dummy_icid;
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
FCOE_RAMROD_CMD_ID_DESTROY_FUNC,
PROTOCOLID_FCOE, &init_data);
if (rc)
return rc;
active_segs = qed_rd(p_hwfn, p_ptt, TM_REG_PF_ENABLE_TASK);
active_segs &= ~BIT(QED_CXT_FCOE_TID_SEG);
qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_TASK, active_segs);
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static int
qed_fcoe_allocate_connection(struct qed_hwfn *p_hwfn,
struct qed_fcoe_conn **p_out_conn)
{
struct qed_fcoe_conn *p_conn = NULL;
void *p_addr;
u32 i;
spin_lock_bh(&p_hwfn->p_fcoe_info->lock);
if (!list_empty(&p_hwfn->p_fcoe_info->free_list))
p_conn =
list_first_entry(&p_hwfn->p_fcoe_info->free_list,
struct qed_fcoe_conn, list_entry);
if (p_conn) {
list_del(&p_conn->list_entry);
spin_unlock_bh(&p_hwfn->p_fcoe_info->lock);
*p_out_conn = p_conn;
return 0;
}
spin_unlock_bh(&p_hwfn->p_fcoe_info->lock);
p_conn = kzalloc(sizeof(*p_conn), GFP_KERNEL);
if (!p_conn)
return -ENOMEM;
p_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
&p_conn->xferq_pbl_addr, GFP_KERNEL);
if (!p_addr)
goto nomem_pbl_xferq;
p_conn->xferq_pbl_addr_virt_addr = p_addr;
for (i = 0; i < ARRAY_SIZE(p_conn->xferq_addr); i++) {
p_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
&p_conn->xferq_addr[i], GFP_KERNEL);
if (!p_addr)
goto nomem_xferq;
p_conn->xferq_addr_virt_addr[i] = p_addr;
p_addr = p_conn->xferq_pbl_addr_virt_addr;
((dma_addr_t *)p_addr)[i] = p_conn->xferq_addr[i];
}
p_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
&p_conn->confq_pbl_addr, GFP_KERNEL);
if (!p_addr)
goto nomem_xferq;
p_conn->confq_pbl_addr_virt_addr = p_addr;
for (i = 0; i < ARRAY_SIZE(p_conn->confq_addr); i++) {
p_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
&p_conn->confq_addr[i], GFP_KERNEL);
if (!p_addr)
goto nomem_confq;
p_conn->confq_addr_virt_addr[i] = p_addr;
p_addr = p_conn->confq_pbl_addr_virt_addr;
((dma_addr_t *)p_addr)[i] = p_conn->confq_addr[i];
}
p_conn->free_on_delete = true;
*p_out_conn = p_conn;
return 0;
nomem_confq:
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->confq_pbl_addr_virt_addr,
p_conn->confq_pbl_addr);
for (i = 0; i < ARRAY_SIZE(p_conn->confq_addr); i++)
if (p_conn->confq_addr_virt_addr[i])
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->confq_addr_virt_addr[i],
p_conn->confq_addr[i]);
nomem_xferq:
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->xferq_pbl_addr_virt_addr,
p_conn->xferq_pbl_addr);
for (i = 0; i < ARRAY_SIZE(p_conn->xferq_addr); i++)
if (p_conn->xferq_addr_virt_addr[i])
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->xferq_addr_virt_addr[i],
p_conn->xferq_addr[i]);
nomem_pbl_xferq:
kfree(p_conn);
return -ENOMEM;
}
static void qed_fcoe_free_connection(struct qed_hwfn *p_hwfn,
struct qed_fcoe_conn *p_conn)
{
u32 i;
if (!p_conn)
return;
if (p_conn->confq_pbl_addr_virt_addr)
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->confq_pbl_addr_virt_addr,
p_conn->confq_pbl_addr);
for (i = 0; i < ARRAY_SIZE(p_conn->confq_addr); i++) {
if (!p_conn->confq_addr_virt_addr[i])
continue;
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->confq_addr_virt_addr[i],
p_conn->confq_addr[i]);
}
if (p_conn->xferq_pbl_addr_virt_addr)
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->xferq_pbl_addr_virt_addr,
p_conn->xferq_pbl_addr);
for (i = 0; i < ARRAY_SIZE(p_conn->xferq_addr); i++) {
if (!p_conn->xferq_addr_virt_addr[i])
continue;
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
QED_CHAIN_PAGE_SIZE,
p_conn->xferq_addr_virt_addr[i],
p_conn->xferq_addr[i]);
}
kfree(p_conn);
}
static void __iomem *qed_fcoe_get_db_addr(struct qed_hwfn *p_hwfn, u32 cid)
{
return (u8 __iomem *)p_hwfn->doorbells +
qed_db_addr(cid, DQ_DEMS_LEGACY);
}
static void __iomem *qed_fcoe_get_primary_bdq_prod(struct qed_hwfn *p_hwfn,
u8 bdq_id)
{
if (RESC_NUM(p_hwfn, QED_BDQ)) {
return (u8 __iomem *)p_hwfn->regview +
GTT_BAR0_MAP_REG_MSDM_RAM +
MSTORM_SCSI_BDQ_EXT_PROD_OFFSET(RESC_START(p_hwfn,
QED_BDQ),
bdq_id);
} else {
DP_NOTICE(p_hwfn, "BDQ is not allocated!\n");
return NULL;
}
}
static void __iomem *qed_fcoe_get_secondary_bdq_prod(struct qed_hwfn *p_hwfn,
u8 bdq_id)
{
if (RESC_NUM(p_hwfn, QED_BDQ)) {
return (u8 __iomem *)p_hwfn->regview +
GTT_BAR0_MAP_REG_TSDM_RAM +
TSTORM_SCSI_BDQ_EXT_PROD_OFFSET(RESC_START(p_hwfn,
QED_BDQ),
bdq_id);
} else {
DP_NOTICE(p_hwfn, "BDQ is not allocated!\n");
return NULL;
}
}
struct qed_fcoe_info *qed_fcoe_alloc(struct qed_hwfn *p_hwfn)
{
struct qed_fcoe_info *p_fcoe_info;
/* Allocate LL2's set struct */
p_fcoe_info = kzalloc(sizeof(*p_fcoe_info), GFP_KERNEL);
if (!p_fcoe_info) {
DP_NOTICE(p_hwfn, "Failed to allocate qed_fcoe_info'\n");
return NULL;
}
INIT_LIST_HEAD(&p_fcoe_info->free_list);
return p_fcoe_info;
}
void qed_fcoe_setup(struct qed_hwfn *p_hwfn, struct qed_fcoe_info *p_fcoe_info)
{
struct fcoe_task_context *p_task_ctx = NULL;
int rc;
u32 i;
spin_lock_init(&p_fcoe_info->lock);
for (i = 0; i < p_hwfn->pf_params.fcoe_pf_params.num_tasks; i++) {
rc = qed_cxt_get_task_ctx(p_hwfn, i,
QED_CTX_WORKING_MEM,
(void **)&p_task_ctx);
if (rc)
continue;
memset(p_task_ctx, 0, sizeof(struct fcoe_task_context));
SET_FIELD(p_task_ctx->timer_context.logical_client_0,
TIMERS_CONTEXT_VALIDLC0, 1);
SET_FIELD(p_task_ctx->timer_context.logical_client_1,
TIMERS_CONTEXT_VALIDLC1, 1);
SET_FIELD(p_task_ctx->tstorm_ag_context.flags0,
TSTORM_FCOE_TASK_AG_CTX_CONNECTION_TYPE, 1);
}
}
void qed_fcoe_free(struct qed_hwfn *p_hwfn, struct qed_fcoe_info *p_fcoe_info)
{
struct qed_fcoe_conn *p_conn = NULL;
if (!p_fcoe_info)
return;
while (!list_empty(&p_fcoe_info->free_list)) {
p_conn = list_first_entry(&p_fcoe_info->free_list,
struct qed_fcoe_conn, list_entry);
if (!p_conn)
break;
list_del(&p_conn->list_entry);
qed_fcoe_free_connection(p_hwfn, p_conn);
}
kfree(p_fcoe_info);
}
static int
qed_fcoe_acquire_connection(struct qed_hwfn *p_hwfn,
struct qed_fcoe_conn *p_in_conn,
struct qed_fcoe_conn **p_out_conn)
{
struct qed_fcoe_conn *p_conn = NULL;
int rc = 0;
u32 icid;
spin_lock_bh(&p_hwfn->p_fcoe_info->lock);
rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_FCOE, &icid);
spin_unlock_bh(&p_hwfn->p_fcoe_info->lock);
if (rc)
return rc;
/* Use input connection [if provided] or allocate a new one */
if (p_in_conn) {
p_conn = p_in_conn;
} else {
rc = qed_fcoe_allocate_connection(p_hwfn, &p_conn);
if (rc) {
spin_lock_bh(&p_hwfn->p_fcoe_info->lock);
qed_cxt_release_cid(p_hwfn, icid);
spin_unlock_bh(&p_hwfn->p_fcoe_info->lock);
return rc;
}
}
p_conn->icid = icid;
p_conn->fw_cid = (p_hwfn->hw_info.opaque_fid << 16) | icid;
*p_out_conn = p_conn;
return rc;
}
static void qed_fcoe_release_connection(struct qed_hwfn *p_hwfn,
struct qed_fcoe_conn *p_conn)
{
spin_lock_bh(&p_hwfn->p_fcoe_info->lock);
list_add_tail(&p_conn->list_entry, &p_hwfn->p_fcoe_info->free_list);
qed_cxt_release_cid(p_hwfn, p_conn->icid);
spin_unlock_bh(&p_hwfn->p_fcoe_info->lock);
}
static void _qed_fcoe_get_tstats(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_fcoe_stats *p_stats)
{
struct fcoe_rx_stat tstats;
u32 tstats_addr;
memset(&tstats, 0, sizeof(tstats));
tstats_addr = BAR0_MAP_REG_TSDM_RAM +
TSTORM_FCOE_RX_STATS_OFFSET(p_hwfn->rel_pf_id);
qed_memcpy_from(p_hwfn, p_ptt, &tstats, tstats_addr, sizeof(tstats));
p_stats->fcoe_rx_byte_cnt = HILO_64_REGPAIR(tstats.fcoe_rx_byte_cnt);
p_stats->fcoe_rx_data_pkt_cnt =
HILO_64_REGPAIR(tstats.fcoe_rx_data_pkt_cnt);
p_stats->fcoe_rx_xfer_pkt_cnt =
HILO_64_REGPAIR(tstats.fcoe_rx_xfer_pkt_cnt);
p_stats->fcoe_rx_other_pkt_cnt =
HILO_64_REGPAIR(tstats.fcoe_rx_other_pkt_cnt);
p_stats->fcoe_silent_drop_pkt_cmdq_full_cnt =
le32_to_cpu(tstats.fcoe_silent_drop_pkt_cmdq_full_cnt);
p_stats->fcoe_silent_drop_pkt_rq_full_cnt =
le32_to_cpu(tstats.fcoe_silent_drop_pkt_rq_full_cnt);
p_stats->fcoe_silent_drop_pkt_crc_error_cnt =
le32_to_cpu(tstats.fcoe_silent_drop_pkt_crc_error_cnt);
p_stats->fcoe_silent_drop_pkt_task_invalid_cnt =
le32_to_cpu(tstats.fcoe_silent_drop_pkt_task_invalid_cnt);
p_stats->fcoe_silent_drop_total_pkt_cnt =
le32_to_cpu(tstats.fcoe_silent_drop_total_pkt_cnt);
}
static void _qed_fcoe_get_pstats(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_fcoe_stats *p_stats)
{
struct fcoe_tx_stat pstats;
u32 pstats_addr;
memset(&pstats, 0, sizeof(pstats));
pstats_addr = BAR0_MAP_REG_PSDM_RAM +
PSTORM_FCOE_TX_STATS_OFFSET(p_hwfn->rel_pf_id);
qed_memcpy_from(p_hwfn, p_ptt, &pstats, pstats_addr, sizeof(pstats));
p_stats->fcoe_tx_byte_cnt = HILO_64_REGPAIR(pstats.fcoe_tx_byte_cnt);
p_stats->fcoe_tx_data_pkt_cnt =
HILO_64_REGPAIR(pstats.fcoe_tx_data_pkt_cnt);
p_stats->fcoe_tx_xfer_pkt_cnt =
HILO_64_REGPAIR(pstats.fcoe_tx_xfer_pkt_cnt);
p_stats->fcoe_tx_other_pkt_cnt =
HILO_64_REGPAIR(pstats.fcoe_tx_other_pkt_cnt);
}
static int qed_fcoe_get_stats(struct qed_hwfn *p_hwfn,
struct qed_fcoe_stats *p_stats)
{
struct qed_ptt *p_ptt;
memset(p_stats, 0, sizeof(*p_stats));
p_ptt = qed_ptt_acquire(p_hwfn);
if (!p_ptt) {
DP_ERR(p_hwfn, "Failed to acquire ptt\n");
return -EINVAL;
}
_qed_fcoe_get_tstats(p_hwfn, p_ptt, p_stats);
_qed_fcoe_get_pstats(p_hwfn, p_ptt, p_stats);
qed_ptt_release(p_hwfn, p_ptt);
return 0;
}
struct qed_hash_fcoe_con {
struct hlist_node node;
struct qed_fcoe_conn *con;
};
static int qed_fill_fcoe_dev_info(struct qed_dev *cdev,
struct qed_dev_fcoe_info *info)
{
struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
int rc;
memset(info, 0, sizeof(*info));
rc = qed_fill_dev_info(cdev, &info->common);
info->primary_dbq_rq_addr =
qed_fcoe_get_primary_bdq_prod(hwfn, BDQ_ID_RQ);
info->secondary_bdq_rq_addr =
qed_fcoe_get_secondary_bdq_prod(hwfn, BDQ_ID_RQ);
return rc;
}
static void qed_register_fcoe_ops(struct qed_dev *cdev,
struct qed_fcoe_cb_ops *ops, void *cookie)
{
cdev->protocol_ops.fcoe = ops;
cdev->ops_cookie = cookie;
}
static struct qed_hash_fcoe_con *qed_fcoe_get_hash(struct qed_dev *cdev,
u32 handle)
{
struct qed_hash_fcoe_con *hash_con = NULL;
if (!(cdev->flags & QED_FLAG_STORAGE_STARTED))
return NULL;
hash_for_each_possible(cdev->connections, hash_con, node, handle) {
if (hash_con->con->icid == handle)
break;
}
if (!hash_con || (hash_con->con->icid != handle))
return NULL;
return hash_con;
}
static int qed_fcoe_stop(struct qed_dev *cdev)
{
qed: Don't use main-ptt in unrelated flows In order to access HW registers driver needs to acquire a PTT entry [mapping between bar memory and internal chip address]. Since acquiring PTT entries could fail [at least in theory] as their number is finite and other flows can hold them, we reserve special PTT entries for 'important' enough flows - ones we want to guarantee that would not be susceptible to such issues. One such special entry is the 'main' PTT which is meant to be used in flows such as chip initialization and de-initialization. However, there are other flows that are also using that same entry for their own purpose, and might run concurrently with the original flows [notice that for most cases using the main-ptt by mistake, such a race is still impossible, at least today]. This patch re-organizes the various functions that currently use the main_ptt in one of two ways: - If a function shouldn't use the main_ptt it starts acquiring and releasing it's own PTT entry and use it instead. Notice if those functions previously couldn't fail, they now can [as acquisition might fail]. - Change the prototypes so that the main_ptt would be received as a parameter [instead of explicitly accessing it]. This prevents the future risk of adding codes that introduces new use-cases for flows using the main_ptt, ones that might be in race with the actual 'main' flows. Signed-off-by: Rahul Verma <Rahul.Verma@cavium.com> Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2017-04-06 20:58:29 +08:00
struct qed_ptt *p_ptt;
int rc;
if (!(cdev->flags & QED_FLAG_STORAGE_STARTED)) {
DP_NOTICE(cdev, "fcoe already stopped\n");
return 0;
}
if (!hash_empty(cdev->connections)) {
DP_NOTICE(cdev,
"Can't stop fcoe - not all connections were returned\n");
return -EINVAL;
}
qed: Don't use main-ptt in unrelated flows In order to access HW registers driver needs to acquire a PTT entry [mapping between bar memory and internal chip address]. Since acquiring PTT entries could fail [at least in theory] as their number is finite and other flows can hold them, we reserve special PTT entries for 'important' enough flows - ones we want to guarantee that would not be susceptible to such issues. One such special entry is the 'main' PTT which is meant to be used in flows such as chip initialization and de-initialization. However, there are other flows that are also using that same entry for their own purpose, and might run concurrently with the original flows [notice that for most cases using the main-ptt by mistake, such a race is still impossible, at least today]. This patch re-organizes the various functions that currently use the main_ptt in one of two ways: - If a function shouldn't use the main_ptt it starts acquiring and releasing it's own PTT entry and use it instead. Notice if those functions previously couldn't fail, they now can [as acquisition might fail]. - Change the prototypes so that the main_ptt would be received as a parameter [instead of explicitly accessing it]. This prevents the future risk of adding codes that introduces new use-cases for flows using the main_ptt, ones that might be in race with the actual 'main' flows. Signed-off-by: Rahul Verma <Rahul.Verma@cavium.com> Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2017-04-06 20:58:29 +08:00
p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev));
if (!p_ptt)
return -EAGAIN;
/* Stop the fcoe */
qed: Don't use main-ptt in unrelated flows In order to access HW registers driver needs to acquire a PTT entry [mapping between bar memory and internal chip address]. Since acquiring PTT entries could fail [at least in theory] as their number is finite and other flows can hold them, we reserve special PTT entries for 'important' enough flows - ones we want to guarantee that would not be susceptible to such issues. One such special entry is the 'main' PTT which is meant to be used in flows such as chip initialization and de-initialization. However, there are other flows that are also using that same entry for their own purpose, and might run concurrently with the original flows [notice that for most cases using the main-ptt by mistake, such a race is still impossible, at least today]. This patch re-organizes the various functions that currently use the main_ptt in one of two ways: - If a function shouldn't use the main_ptt it starts acquiring and releasing it's own PTT entry and use it instead. Notice if those functions previously couldn't fail, they now can [as acquisition might fail]. - Change the prototypes so that the main_ptt would be received as a parameter [instead of explicitly accessing it]. This prevents the future risk of adding codes that introduces new use-cases for flows using the main_ptt, ones that might be in race with the actual 'main' flows. Signed-off-by: Rahul Verma <Rahul.Verma@cavium.com> Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2017-04-06 20:58:29 +08:00
rc = qed_sp_fcoe_func_stop(QED_LEADING_HWFN(cdev), p_ptt,
QED_SPQ_MODE_EBLOCK, NULL);
cdev->flags &= ~QED_FLAG_STORAGE_STARTED;
qed: Don't use main-ptt in unrelated flows In order to access HW registers driver needs to acquire a PTT entry [mapping between bar memory and internal chip address]. Since acquiring PTT entries could fail [at least in theory] as their number is finite and other flows can hold them, we reserve special PTT entries for 'important' enough flows - ones we want to guarantee that would not be susceptible to such issues. One such special entry is the 'main' PTT which is meant to be used in flows such as chip initialization and de-initialization. However, there are other flows that are also using that same entry for their own purpose, and might run concurrently with the original flows [notice that for most cases using the main-ptt by mistake, such a race is still impossible, at least today]. This patch re-organizes the various functions that currently use the main_ptt in one of two ways: - If a function shouldn't use the main_ptt it starts acquiring and releasing it's own PTT entry and use it instead. Notice if those functions previously couldn't fail, they now can [as acquisition might fail]. - Change the prototypes so that the main_ptt would be received as a parameter [instead of explicitly accessing it]. This prevents the future risk of adding codes that introduces new use-cases for flows using the main_ptt, ones that might be in race with the actual 'main' flows. Signed-off-by: Rahul Verma <Rahul.Verma@cavium.com> Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2017-04-06 20:58:29 +08:00
qed_ptt_release(QED_LEADING_HWFN(cdev), p_ptt);
return rc;
}
static int qed_fcoe_start(struct qed_dev *cdev, struct qed_fcoe_tid *tasks)
{
int rc;
if (cdev->flags & QED_FLAG_STORAGE_STARTED) {
DP_NOTICE(cdev, "fcoe already started;\n");
return 0;
}
rc = qed_sp_fcoe_func_start(QED_LEADING_HWFN(cdev),
QED_SPQ_MODE_EBLOCK, NULL);
if (rc) {
DP_NOTICE(cdev, "Failed to start fcoe\n");
return rc;
}
cdev->flags |= QED_FLAG_STORAGE_STARTED;
hash_init(cdev->connections);
if (tasks) {
struct qed_tid_mem *tid_info = kzalloc(sizeof(*tid_info),
GFP_ATOMIC);
if (!tid_info) {
DP_NOTICE(cdev,
"Failed to allocate tasks information\n");
qed_fcoe_stop(cdev);
return -ENOMEM;
}
rc = qed_cxt_get_tid_mem_info(QED_LEADING_HWFN(cdev), tid_info);
if (rc) {
DP_NOTICE(cdev, "Failed to gather task information\n");
qed_fcoe_stop(cdev);
kfree(tid_info);
return rc;
}
/* Fill task information */
tasks->size = tid_info->tid_size;
tasks->num_tids_per_block = tid_info->num_tids_per_block;
memcpy(tasks->blocks, tid_info->blocks,
MAX_TID_BLOCKS_FCOE * sizeof(u8 *));
kfree(tid_info);
}
return 0;
}
static int qed_fcoe_acquire_conn(struct qed_dev *cdev,
u32 *handle,
u32 *fw_cid, void __iomem **p_doorbell)
{
struct qed_hash_fcoe_con *hash_con;
int rc;
/* Allocate a hashed connection */
hash_con = kzalloc(sizeof(*hash_con), GFP_KERNEL);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to allocate hashed connection\n");
return -ENOMEM;
}
/* Acquire the connection */
rc = qed_fcoe_acquire_connection(QED_LEADING_HWFN(cdev), NULL,
&hash_con->con);
if (rc) {
DP_NOTICE(cdev, "Failed to acquire Connection\n");
kfree(hash_con);
return rc;
}
/* Added the connection to hash table */
*handle = hash_con->con->icid;
*fw_cid = hash_con->con->fw_cid;
hash_add(cdev->connections, &hash_con->node, *handle);
if (p_doorbell)
*p_doorbell = qed_fcoe_get_db_addr(QED_LEADING_HWFN(cdev),
*handle);
return 0;
}
static int qed_fcoe_release_conn(struct qed_dev *cdev, u32 handle)
{
struct qed_hash_fcoe_con *hash_con;
hash_con = qed_fcoe_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
hlist_del(&hash_con->node);
qed_fcoe_release_connection(QED_LEADING_HWFN(cdev), hash_con->con);
kfree(hash_con);
return 0;
}
static int qed_fcoe_offload_conn(struct qed_dev *cdev,
u32 handle,
struct qed_fcoe_params_offload *conn_info)
{
struct qed_hash_fcoe_con *hash_con;
struct qed_fcoe_conn *con;
hash_con = qed_fcoe_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
/* Update the connection with information from the params */
con = hash_con->con;
con->sq_pbl_addr = conn_info->sq_pbl_addr;
con->sq_curr_page_addr = conn_info->sq_curr_page_addr;
con->sq_next_page_addr = conn_info->sq_next_page_addr;
con->tx_max_fc_pay_len = conn_info->tx_max_fc_pay_len;
con->e_d_tov_timer_val = conn_info->e_d_tov_timer_val;
con->rec_tov_timer_val = conn_info->rec_tov_timer_val;
con->rx_max_fc_pay_len = conn_info->rx_max_fc_pay_len;
con->vlan_tag = conn_info->vlan_tag;
con->max_conc_seqs_c3 = conn_info->max_conc_seqs_c3;
con->flags = conn_info->flags;
con->def_q_idx = conn_info->def_q_idx;
con->src_mac_addr_hi = (conn_info->src_mac[5] << 8) |
conn_info->src_mac[4];
con->src_mac_addr_mid = (conn_info->src_mac[3] << 8) |
conn_info->src_mac[2];
con->src_mac_addr_lo = (conn_info->src_mac[1] << 8) |
conn_info->src_mac[0];
con->dst_mac_addr_hi = (conn_info->dst_mac[5] << 8) |
conn_info->dst_mac[4];
con->dst_mac_addr_mid = (conn_info->dst_mac[3] << 8) |
conn_info->dst_mac[2];
con->dst_mac_addr_lo = (conn_info->dst_mac[1] << 8) |
conn_info->dst_mac[0];
con->s_id.addr_hi = conn_info->s_id.addr_hi;
con->s_id.addr_mid = conn_info->s_id.addr_mid;
con->s_id.addr_lo = conn_info->s_id.addr_lo;
con->d_id.addr_hi = conn_info->d_id.addr_hi;
con->d_id.addr_mid = conn_info->d_id.addr_mid;
con->d_id.addr_lo = conn_info->d_id.addr_lo;
return qed_sp_fcoe_conn_offload(QED_LEADING_HWFN(cdev), con,
QED_SPQ_MODE_EBLOCK, NULL);
}
static int qed_fcoe_destroy_conn(struct qed_dev *cdev,
u32 handle, dma_addr_t terminate_params)
{
struct qed_hash_fcoe_con *hash_con;
struct qed_fcoe_conn *con;
hash_con = qed_fcoe_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
/* Update the connection with information from the params */
con = hash_con->con;
con->terminate_params = terminate_params;
return qed_sp_fcoe_conn_destroy(QED_LEADING_HWFN(cdev), con,
QED_SPQ_MODE_EBLOCK, NULL);
}
static int qed_fcoe_stats(struct qed_dev *cdev, struct qed_fcoe_stats *stats)
{
return qed_fcoe_get_stats(QED_LEADING_HWFN(cdev), stats);
}
void qed_get_protocol_stats_fcoe(struct qed_dev *cdev,
struct qed_mcp_fcoe_stats *stats)
{
struct qed_fcoe_stats proto_stats;
/* Retrieve FW statistics */
memset(&proto_stats, 0, sizeof(proto_stats));
if (qed_fcoe_stats(cdev, &proto_stats)) {
DP_VERBOSE(cdev, QED_MSG_STORAGE,
"Failed to collect FCoE statistics\n");
return;
}
/* Translate FW statistics into struct */
stats->rx_pkts = proto_stats.fcoe_rx_data_pkt_cnt +
proto_stats.fcoe_rx_xfer_pkt_cnt +
proto_stats.fcoe_rx_other_pkt_cnt;
stats->tx_pkts = proto_stats.fcoe_tx_data_pkt_cnt +
proto_stats.fcoe_tx_xfer_pkt_cnt +
proto_stats.fcoe_tx_other_pkt_cnt;
stats->fcs_err = proto_stats.fcoe_silent_drop_pkt_crc_error_cnt;
/* Request protocol driver to fill-in the rest */
if (cdev->protocol_ops.fcoe && cdev->ops_cookie) {
struct qed_fcoe_cb_ops *ops = cdev->protocol_ops.fcoe;
void *cookie = cdev->ops_cookie;
if (ops->get_login_failures)
stats->login_failure = ops->get_login_failures(cookie);
}
}
static const struct qed_fcoe_ops qed_fcoe_ops_pass = {
.common = &qed_common_ops_pass,
.ll2 = &qed_ll2_ops_pass,
.fill_dev_info = &qed_fill_fcoe_dev_info,
.start = &qed_fcoe_start,
.stop = &qed_fcoe_stop,
.register_ops = &qed_register_fcoe_ops,
.acquire_conn = &qed_fcoe_acquire_conn,
.release_conn = &qed_fcoe_release_conn,
.offload_conn = &qed_fcoe_offload_conn,
.destroy_conn = &qed_fcoe_destroy_conn,
.get_stats = &qed_fcoe_stats,
};
const struct qed_fcoe_ops *qed_get_fcoe_ops(void)
{
return &qed_fcoe_ops_pass;
}
EXPORT_SYMBOL(qed_get_fcoe_ops);
void qed_put_fcoe_ops(void)
{
}
EXPORT_SYMBOL(qed_put_fcoe_ops);