cae82d4999
o Linux stack estimates MSS from skb->len or skb_shinfo(skb)->gso_size. In case of LRO skb->len is aggregate of len of number of packets hence MSS obtained using skb->len would be incorrect. Incorrect estimation of recv MSS would lead to delayed acks in some traffic patterns (which sends two or three packets and wait for ack and only then send remaining packets). This leads to drop in performance. Hence we need to set gso_size to MSS obtained from firmware. o This is fixed recently in firmware hence the MSS is obtained based on capability. If fw is capable of sending the MSS then only driver sets the gso_size. Signed-off-by: Rajesh Borundia <rajesh.borundia@qlogic.com> Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1190 lines
32 KiB
C
1190 lines
32 KiB
C
/*
|
|
* QLogic qlcnic NIC Driver
|
|
* Copyright (c) 2009-2010 QLogic Corporation
|
|
*
|
|
* See LICENSE.qlcnic for copyright and licensing details.
|
|
*/
|
|
|
|
#include "qlcnic.h"
|
|
|
|
static u32
|
|
qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
|
|
{
|
|
u32 rsp;
|
|
int timeout = 0;
|
|
|
|
do {
|
|
/* give atleast 1ms for firmware to respond */
|
|
msleep(1);
|
|
|
|
if (++timeout > QLCNIC_OS_CRB_RETRY_COUNT)
|
|
return QLCNIC_CDRP_RSP_TIMEOUT;
|
|
|
|
rsp = QLCRD32(adapter, QLCNIC_CDRP_CRB_OFFSET);
|
|
} while (!QLCNIC_CDRP_IS_RSP(rsp));
|
|
|
|
return rsp;
|
|
}
|
|
|
|
void
|
|
qlcnic_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *cmd)
|
|
{
|
|
u32 rsp;
|
|
u32 signature;
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
|
|
signature = QLCNIC_CDRP_SIGNATURE_MAKE(ahw->pci_func,
|
|
adapter->fw_hal_version);
|
|
|
|
/* Acquire semaphore before accessing CRB */
|
|
if (qlcnic_api_lock(adapter)) {
|
|
cmd->rsp.cmd = QLCNIC_RCODE_TIMEOUT;
|
|
return;
|
|
}
|
|
|
|
QLCWR32(adapter, QLCNIC_SIGN_CRB_OFFSET, signature);
|
|
QLCWR32(adapter, QLCNIC_ARG1_CRB_OFFSET, cmd->req.arg1);
|
|
QLCWR32(adapter, QLCNIC_ARG2_CRB_OFFSET, cmd->req.arg2);
|
|
QLCWR32(adapter, QLCNIC_ARG3_CRB_OFFSET, cmd->req.arg3);
|
|
QLCWR32(adapter, QLCNIC_CDRP_CRB_OFFSET,
|
|
QLCNIC_CDRP_FORM_CMD(cmd->req.cmd));
|
|
|
|
rsp = qlcnic_poll_rsp(adapter);
|
|
|
|
if (rsp == QLCNIC_CDRP_RSP_TIMEOUT) {
|
|
dev_err(&pdev->dev, "card response timeout.\n");
|
|
cmd->rsp.cmd = QLCNIC_RCODE_TIMEOUT;
|
|
} else if (rsp == QLCNIC_CDRP_RSP_FAIL) {
|
|
cmd->rsp.cmd = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
|
|
dev_err(&pdev->dev, "failed card response code:0x%x\n",
|
|
cmd->rsp.cmd);
|
|
} else if (rsp == QLCNIC_CDRP_RSP_OK) {
|
|
cmd->rsp.cmd = QLCNIC_RCODE_SUCCESS;
|
|
if (cmd->rsp.arg2)
|
|
cmd->rsp.arg2 = QLCRD32(adapter,
|
|
QLCNIC_ARG2_CRB_OFFSET);
|
|
if (cmd->rsp.arg3)
|
|
cmd->rsp.arg3 = QLCRD32(adapter,
|
|
QLCNIC_ARG3_CRB_OFFSET);
|
|
}
|
|
if (cmd->rsp.arg1)
|
|
cmd->rsp.arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
|
|
|
|
/* Release semaphore */
|
|
qlcnic_api_unlock(adapter);
|
|
|
|
}
|
|
|
|
static uint32_t qlcnic_temp_checksum(uint32_t *temp_buffer, u16 temp_size)
|
|
{
|
|
uint64_t sum = 0;
|
|
int count = temp_size / sizeof(uint32_t);
|
|
while (count-- > 0)
|
|
sum += *temp_buffer++;
|
|
while (sum >> 32)
|
|
sum = (sum & 0xFFFFFFFF) + (sum >> 32);
|
|
return ~sum;
|
|
}
|
|
|
|
int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
|
|
{
|
|
int err, i;
|
|
u16 temp_size;
|
|
void *tmp_addr;
|
|
u32 version, csum, *template, *tmp_buf;
|
|
struct qlcnic_cmd_args cmd;
|
|
struct qlcnic_hardware_context *ahw;
|
|
struct qlcnic_dump_template_hdr *tmpl_hdr, *tmp_tmpl;
|
|
dma_addr_t tmp_addr_t = 0;
|
|
|
|
ahw = adapter->ahw;
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_TEMP_SIZE;
|
|
memset(&cmd.rsp, 1, sizeof(struct _cdrp_cmd));
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
if (cmd.rsp.cmd != QLCNIC_RCODE_SUCCESS) {
|
|
dev_info(&adapter->pdev->dev,
|
|
"Can't get template size %d\n", cmd.rsp.cmd);
|
|
err = -EIO;
|
|
return err;
|
|
}
|
|
temp_size = cmd.rsp.arg2;
|
|
version = cmd.rsp.arg3;
|
|
if (!temp_size)
|
|
return -EIO;
|
|
|
|
tmp_addr = dma_alloc_coherent(&adapter->pdev->dev, temp_size,
|
|
&tmp_addr_t, GFP_KERNEL);
|
|
if (!tmp_addr) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Can't get memory for FW dump template\n");
|
|
return -ENOMEM;
|
|
}
|
|
memset(&cmd.rsp, 0, sizeof(struct _cdrp_cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_GET_TEMP_HDR;
|
|
cmd.req.arg1 = LSD(tmp_addr_t);
|
|
cmd.req.arg2 = MSD(tmp_addr_t);
|
|
cmd.req.arg3 = temp_size;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
err = cmd.rsp.cmd;
|
|
if (err != QLCNIC_RCODE_SUCCESS) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get mini dump template header %d\n", err);
|
|
err = -EIO;
|
|
goto error;
|
|
}
|
|
tmp_tmpl = tmp_addr;
|
|
csum = qlcnic_temp_checksum((uint32_t *) tmp_addr, temp_size);
|
|
if (csum) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Template header checksum validation failed\n");
|
|
err = -EIO;
|
|
goto error;
|
|
}
|
|
ahw->fw_dump.tmpl_hdr = vzalloc(temp_size);
|
|
if (!ahw->fw_dump.tmpl_hdr) {
|
|
err = -EIO;
|
|
goto error;
|
|
}
|
|
tmp_buf = tmp_addr;
|
|
template = (u32 *) ahw->fw_dump.tmpl_hdr;
|
|
for (i = 0; i < temp_size/sizeof(u32); i++)
|
|
*template++ = __le32_to_cpu(*tmp_buf++);
|
|
|
|
tmpl_hdr = ahw->fw_dump.tmpl_hdr;
|
|
tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF;
|
|
ahw->fw_dump.enable = 1;
|
|
error:
|
|
dma_free_coherent(&adapter->pdev->dev, temp_size, tmp_addr, tmp_addr_t);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
|
|
{
|
|
struct qlcnic_cmd_args cmd;
|
|
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_SET_MTU;
|
|
cmd.req.arg1 = recv_ctx->context_id;
|
|
cmd.req.arg2 = mtu;
|
|
cmd.req.arg3 = 0;
|
|
if (recv_ctx->state == QLCNIC_HOST_CTX_STATE_ACTIVE) {
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
if (cmd.rsp.cmd) {
|
|
dev_err(&adapter->pdev->dev, "Failed to set mtu\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
void *addr;
|
|
struct qlcnic_hostrq_rx_ctx *prq;
|
|
struct qlcnic_cardrsp_rx_ctx *prsp;
|
|
struct qlcnic_hostrq_rds_ring *prq_rds;
|
|
struct qlcnic_hostrq_sds_ring *prq_sds;
|
|
struct qlcnic_cardrsp_rds_ring *prsp_rds;
|
|
struct qlcnic_cardrsp_sds_ring *prsp_sds;
|
|
struct qlcnic_host_rds_ring *rds_ring;
|
|
struct qlcnic_host_sds_ring *sds_ring;
|
|
struct qlcnic_cmd_args cmd;
|
|
|
|
dma_addr_t hostrq_phys_addr, cardrsp_phys_addr;
|
|
u64 phys_addr;
|
|
|
|
u8 i, nrds_rings, nsds_rings;
|
|
size_t rq_size, rsp_size;
|
|
u32 cap, reg, val, reg2;
|
|
int err;
|
|
|
|
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
|
|
|
|
nrds_rings = adapter->max_rds_rings;
|
|
nsds_rings = adapter->max_sds_rings;
|
|
|
|
rq_size =
|
|
SIZEOF_HOSTRQ_RX(struct qlcnic_hostrq_rx_ctx, nrds_rings,
|
|
nsds_rings);
|
|
rsp_size =
|
|
SIZEOF_CARDRSP_RX(struct qlcnic_cardrsp_rx_ctx, nrds_rings,
|
|
nsds_rings);
|
|
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size,
|
|
&hostrq_phys_addr, GFP_KERNEL);
|
|
if (addr == NULL)
|
|
return -ENOMEM;
|
|
prq = addr;
|
|
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
|
|
&cardrsp_phys_addr, GFP_KERNEL);
|
|
if (addr == NULL) {
|
|
err = -ENOMEM;
|
|
goto out_free_rq;
|
|
}
|
|
prsp = addr;
|
|
|
|
prq->host_rsp_dma_addr = cpu_to_le64(cardrsp_phys_addr);
|
|
|
|
cap = (QLCNIC_CAP0_LEGACY_CONTEXT | QLCNIC_CAP0_LEGACY_MN
|
|
| QLCNIC_CAP0_VALIDOFF);
|
|
cap |= (QLCNIC_CAP0_JUMBO_CONTIGUOUS | QLCNIC_CAP0_LRO_CONTIGUOUS);
|
|
|
|
if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP)
|
|
cap |= QLCNIC_CAP0_LRO_MSS;
|
|
|
|
prq->valid_field_offset = offsetof(struct qlcnic_hostrq_rx_ctx,
|
|
msix_handler);
|
|
prq->txrx_sds_binding = nsds_rings - 1;
|
|
|
|
prq->capabilities[0] = cpu_to_le32(cap);
|
|
prq->host_int_crb_mode =
|
|
cpu_to_le32(QLCNIC_HOST_INT_CRB_MODE_SHARED);
|
|
prq->host_rds_crb_mode =
|
|
cpu_to_le32(QLCNIC_HOST_RDS_CRB_MODE_UNIQUE);
|
|
|
|
prq->num_rds_rings = cpu_to_le16(nrds_rings);
|
|
prq->num_sds_rings = cpu_to_le16(nsds_rings);
|
|
prq->rds_ring_offset = 0;
|
|
|
|
val = le32_to_cpu(prq->rds_ring_offset) +
|
|
(sizeof(struct qlcnic_hostrq_rds_ring) * nrds_rings);
|
|
prq->sds_ring_offset = cpu_to_le32(val);
|
|
|
|
prq_rds = (struct qlcnic_hostrq_rds_ring *)(prq->data +
|
|
le32_to_cpu(prq->rds_ring_offset));
|
|
|
|
for (i = 0; i < nrds_rings; i++) {
|
|
|
|
rds_ring = &recv_ctx->rds_rings[i];
|
|
rds_ring->producer = 0;
|
|
|
|
prq_rds[i].host_phys_addr = cpu_to_le64(rds_ring->phys_addr);
|
|
prq_rds[i].ring_size = cpu_to_le32(rds_ring->num_desc);
|
|
prq_rds[i].ring_kind = cpu_to_le32(i);
|
|
prq_rds[i].buff_size = cpu_to_le64(rds_ring->dma_size);
|
|
}
|
|
|
|
prq_sds = (struct qlcnic_hostrq_sds_ring *)(prq->data +
|
|
le32_to_cpu(prq->sds_ring_offset));
|
|
|
|
for (i = 0; i < nsds_rings; i++) {
|
|
|
|
sds_ring = &recv_ctx->sds_rings[i];
|
|
sds_ring->consumer = 0;
|
|
memset(sds_ring->desc_head, 0, STATUS_DESC_RINGSIZE(sds_ring));
|
|
|
|
prq_sds[i].host_phys_addr = cpu_to_le64(sds_ring->phys_addr);
|
|
prq_sds[i].ring_size = cpu_to_le32(sds_ring->num_desc);
|
|
prq_sds[i].msi_index = cpu_to_le16(i);
|
|
}
|
|
|
|
phys_addr = hostrq_phys_addr;
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.arg1 = (u32) (phys_addr >> 32);
|
|
cmd.req.arg2 = (u32) (phys_addr & 0xffffffff);
|
|
cmd.req.arg3 = rq_size;
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_CREATE_RX_CTX;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
if (err) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to create rx ctx in firmware%d\n", err);
|
|
goto out_free_rsp;
|
|
}
|
|
|
|
|
|
prsp_rds = ((struct qlcnic_cardrsp_rds_ring *)
|
|
&prsp->data[le32_to_cpu(prsp->rds_ring_offset)]);
|
|
|
|
for (i = 0; i < le16_to_cpu(prsp->num_rds_rings); i++) {
|
|
rds_ring = &recv_ctx->rds_rings[i];
|
|
|
|
reg = le32_to_cpu(prsp_rds[i].host_producer_crb);
|
|
rds_ring->crb_rcv_producer = adapter->ahw->pci_base0 + reg;
|
|
}
|
|
|
|
prsp_sds = ((struct qlcnic_cardrsp_sds_ring *)
|
|
&prsp->data[le32_to_cpu(prsp->sds_ring_offset)]);
|
|
|
|
for (i = 0; i < le16_to_cpu(prsp->num_sds_rings); i++) {
|
|
sds_ring = &recv_ctx->sds_rings[i];
|
|
|
|
reg = le32_to_cpu(prsp_sds[i].host_consumer_crb);
|
|
reg2 = le32_to_cpu(prsp_sds[i].interrupt_crb);
|
|
|
|
sds_ring->crb_sts_consumer = adapter->ahw->pci_base0 + reg;
|
|
sds_ring->crb_intr_mask = adapter->ahw->pci_base0 + reg2;
|
|
}
|
|
|
|
recv_ctx->state = le32_to_cpu(prsp->host_ctx_state);
|
|
recv_ctx->context_id = le16_to_cpu(prsp->context_id);
|
|
recv_ctx->virt_port = prsp->virt_port;
|
|
|
|
out_free_rsp:
|
|
dma_free_coherent(&adapter->pdev->dev, rsp_size, prsp,
|
|
cardrsp_phys_addr);
|
|
out_free_rq:
|
|
dma_free_coherent(&adapter->pdev->dev, rq_size, prq, hostrq_phys_addr);
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
qlcnic_fw_cmd_destroy_rx_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
struct qlcnic_cmd_args cmd;
|
|
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.arg1 = recv_ctx->context_id;
|
|
cmd.req.arg2 = QLCNIC_DESTROY_CTX_RESET;
|
|
cmd.req.arg3 = 0;
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_DESTROY_RX_CTX;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
if (cmd.rsp.cmd)
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to destroy rx ctx in firmware\n");
|
|
|
|
recv_ctx->state = QLCNIC_HOST_CTX_STATE_FREED;
|
|
}
|
|
|
|
static int
|
|
qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
struct qlcnic_hostrq_tx_ctx *prq;
|
|
struct qlcnic_hostrq_cds_ring *prq_cds;
|
|
struct qlcnic_cardrsp_tx_ctx *prsp;
|
|
void *rq_addr, *rsp_addr;
|
|
size_t rq_size, rsp_size;
|
|
u32 temp;
|
|
struct qlcnic_cmd_args cmd;
|
|
int err;
|
|
u64 phys_addr;
|
|
dma_addr_t rq_phys_addr, rsp_phys_addr;
|
|
struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
|
|
|
|
/* reset host resources */
|
|
tx_ring->producer = 0;
|
|
tx_ring->sw_consumer = 0;
|
|
*(tx_ring->hw_consumer) = 0;
|
|
|
|
rq_size = SIZEOF_HOSTRQ_TX(struct qlcnic_hostrq_tx_ctx);
|
|
rq_addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size,
|
|
&rq_phys_addr, GFP_KERNEL);
|
|
if (!rq_addr)
|
|
return -ENOMEM;
|
|
|
|
rsp_size = SIZEOF_CARDRSP_TX(struct qlcnic_cardrsp_tx_ctx);
|
|
rsp_addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
|
|
&rsp_phys_addr, GFP_KERNEL);
|
|
if (!rsp_addr) {
|
|
err = -ENOMEM;
|
|
goto out_free_rq;
|
|
}
|
|
|
|
memset(rq_addr, 0, rq_size);
|
|
prq = rq_addr;
|
|
|
|
memset(rsp_addr, 0, rsp_size);
|
|
prsp = rsp_addr;
|
|
|
|
prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr);
|
|
|
|
temp = (QLCNIC_CAP0_LEGACY_CONTEXT | QLCNIC_CAP0_LEGACY_MN |
|
|
QLCNIC_CAP0_LSO);
|
|
prq->capabilities[0] = cpu_to_le32(temp);
|
|
|
|
prq->host_int_crb_mode =
|
|
cpu_to_le32(QLCNIC_HOST_INT_CRB_MODE_SHARED);
|
|
|
|
prq->interrupt_ctl = 0;
|
|
prq->msi_index = 0;
|
|
prq->cmd_cons_dma_addr = cpu_to_le64(tx_ring->hw_cons_phys_addr);
|
|
|
|
prq_cds = &prq->cds_ring;
|
|
|
|
prq_cds->host_phys_addr = cpu_to_le64(tx_ring->phys_addr);
|
|
prq_cds->ring_size = cpu_to_le32(tx_ring->num_desc);
|
|
|
|
phys_addr = rq_phys_addr;
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.arg1 = (u32)(phys_addr >> 32);
|
|
cmd.req.arg2 = ((u32)phys_addr & 0xffffffff);
|
|
cmd.req.arg3 = rq_size;
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_CREATE_TX_CTX;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS) {
|
|
temp = le32_to_cpu(prsp->cds_ring.host_producer_crb);
|
|
tx_ring->crb_cmd_producer = adapter->ahw->pci_base0 + temp;
|
|
|
|
adapter->tx_context_id =
|
|
le16_to_cpu(prsp->context_id);
|
|
} else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to create tx ctx in firmware%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
|
|
dma_free_coherent(&adapter->pdev->dev, rsp_size, rsp_addr,
|
|
rsp_phys_addr);
|
|
|
|
out_free_rq:
|
|
dma_free_coherent(&adapter->pdev->dev, rq_size, rq_addr, rq_phys_addr);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
qlcnic_fw_cmd_destroy_tx_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
struct qlcnic_cmd_args cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.arg1 = adapter->tx_context_id;
|
|
cmd.req.arg2 = QLCNIC_DESTROY_CTX_RESET;
|
|
cmd.req.arg3 = 0;
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_DESTROY_TX_CTX;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
if (cmd.rsp.cmd)
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to destroy tx ctx in firmware\n");
|
|
}
|
|
|
|
int
|
|
qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config)
|
|
{
|
|
struct qlcnic_cmd_args cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.arg1 = config;
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_CONFIG_PORT;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
return cmd.rsp.cmd;
|
|
}
|
|
|
|
int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
|
|
{
|
|
void *addr;
|
|
int err;
|
|
int ring;
|
|
struct qlcnic_recv_context *recv_ctx;
|
|
struct qlcnic_host_rds_ring *rds_ring;
|
|
struct qlcnic_host_sds_ring *sds_ring;
|
|
struct qlcnic_host_tx_ring *tx_ring;
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
recv_ctx = adapter->recv_ctx;
|
|
tx_ring = adapter->tx_ring;
|
|
|
|
tx_ring->hw_consumer = (__le32 *) dma_alloc_coherent(&pdev->dev,
|
|
sizeof(u32), &tx_ring->hw_cons_phys_addr, GFP_KERNEL);
|
|
if (tx_ring->hw_consumer == NULL) {
|
|
dev_err(&pdev->dev, "failed to allocate tx consumer\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* cmd desc ring */
|
|
addr = dma_alloc_coherent(&pdev->dev, TX_DESC_RINGSIZE(tx_ring),
|
|
&tx_ring->phys_addr, GFP_KERNEL);
|
|
|
|
if (addr == NULL) {
|
|
dev_err(&pdev->dev, "failed to allocate tx desc ring\n");
|
|
err = -ENOMEM;
|
|
goto err_out_free;
|
|
}
|
|
|
|
tx_ring->desc_head = addr;
|
|
|
|
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
|
|
rds_ring = &recv_ctx->rds_rings[ring];
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev,
|
|
RCV_DESC_RINGSIZE(rds_ring),
|
|
&rds_ring->phys_addr, GFP_KERNEL);
|
|
if (addr == NULL) {
|
|
dev_err(&pdev->dev,
|
|
"failed to allocate rds ring [%d]\n", ring);
|
|
err = -ENOMEM;
|
|
goto err_out_free;
|
|
}
|
|
rds_ring->desc_head = addr;
|
|
|
|
}
|
|
|
|
for (ring = 0; ring < adapter->max_sds_rings; ring++) {
|
|
sds_ring = &recv_ctx->sds_rings[ring];
|
|
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev,
|
|
STATUS_DESC_RINGSIZE(sds_ring),
|
|
&sds_ring->phys_addr, GFP_KERNEL);
|
|
if (addr == NULL) {
|
|
dev_err(&pdev->dev,
|
|
"failed to allocate sds ring [%d]\n", ring);
|
|
err = -ENOMEM;
|
|
goto err_out_free;
|
|
}
|
|
sds_ring->desc_head = addr;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out_free:
|
|
qlcnic_free_hw_resources(adapter);
|
|
return err;
|
|
}
|
|
|
|
|
|
int qlcnic_fw_create_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
int err;
|
|
|
|
if (adapter->flags & QLCNIC_NEED_FLR) {
|
|
pci_reset_function(adapter->pdev);
|
|
adapter->flags &= ~QLCNIC_NEED_FLR;
|
|
}
|
|
|
|
err = qlcnic_fw_cmd_create_rx_ctx(adapter);
|
|
if (err)
|
|
return err;
|
|
|
|
err = qlcnic_fw_cmd_create_tx_ctx(adapter);
|
|
if (err) {
|
|
qlcnic_fw_cmd_destroy_rx_ctx(adapter);
|
|
return err;
|
|
}
|
|
|
|
set_bit(__QLCNIC_FW_ATTACHED, &adapter->state);
|
|
return 0;
|
|
}
|
|
|
|
void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) {
|
|
qlcnic_fw_cmd_destroy_rx_ctx(adapter);
|
|
qlcnic_fw_cmd_destroy_tx_ctx(adapter);
|
|
|
|
/* Allow dma queues to drain after context reset */
|
|
msleep(20);
|
|
}
|
|
}
|
|
|
|
void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
|
|
{
|
|
struct qlcnic_recv_context *recv_ctx;
|
|
struct qlcnic_host_rds_ring *rds_ring;
|
|
struct qlcnic_host_sds_ring *sds_ring;
|
|
struct qlcnic_host_tx_ring *tx_ring;
|
|
int ring;
|
|
|
|
recv_ctx = adapter->recv_ctx;
|
|
|
|
tx_ring = adapter->tx_ring;
|
|
if (tx_ring->hw_consumer != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev,
|
|
sizeof(u32),
|
|
tx_ring->hw_consumer,
|
|
tx_ring->hw_cons_phys_addr);
|
|
tx_ring->hw_consumer = NULL;
|
|
}
|
|
|
|
if (tx_ring->desc_head != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev,
|
|
TX_DESC_RINGSIZE(tx_ring),
|
|
tx_ring->desc_head, tx_ring->phys_addr);
|
|
tx_ring->desc_head = NULL;
|
|
}
|
|
|
|
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
|
|
rds_ring = &recv_ctx->rds_rings[ring];
|
|
|
|
if (rds_ring->desc_head != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev,
|
|
RCV_DESC_RINGSIZE(rds_ring),
|
|
rds_ring->desc_head,
|
|
rds_ring->phys_addr);
|
|
rds_ring->desc_head = NULL;
|
|
}
|
|
}
|
|
|
|
for (ring = 0; ring < adapter->max_sds_rings; ring++) {
|
|
sds_ring = &recv_ctx->sds_rings[ring];
|
|
|
|
if (sds_ring->desc_head != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev,
|
|
STATUS_DESC_RINGSIZE(sds_ring),
|
|
sds_ring->desc_head,
|
|
sds_ring->phys_addr);
|
|
sds_ring->desc_head = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Get MAC address of a NIC partition */
|
|
int qlcnic_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac)
|
|
{
|
|
int err;
|
|
struct qlcnic_cmd_args cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.arg1 = adapter->ahw->pci_func | BIT_8;
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_MAC_ADDRESS;
|
|
cmd.rsp.arg1 = cmd.rsp.arg2 = 1;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS)
|
|
qlcnic_fetch_mac(adapter, cmd.rsp.arg1, cmd.rsp.arg2, 0, mac);
|
|
else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get mac address%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Get info of a NIC partition */
|
|
int qlcnic_get_nic_info(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_info *npar_info, u8 func_id)
|
|
{
|
|
int err;
|
|
dma_addr_t nic_dma_t;
|
|
struct qlcnic_info *nic_info;
|
|
void *nic_info_addr;
|
|
struct qlcnic_cmd_args cmd;
|
|
size_t nic_size = sizeof(struct qlcnic_info);
|
|
|
|
nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size,
|
|
&nic_dma_t, GFP_KERNEL);
|
|
if (!nic_info_addr)
|
|
return -ENOMEM;
|
|
memset(nic_info_addr, 0, nic_size);
|
|
|
|
nic_info = nic_info_addr;
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_GET_NIC_INFO;
|
|
cmd.req.arg1 = MSD(nic_dma_t);
|
|
cmd.req.arg2 = LSD(nic_dma_t);
|
|
cmd.req.arg3 = (func_id << 16 | nic_size);
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS) {
|
|
npar_info->pci_func = le16_to_cpu(nic_info->pci_func);
|
|
npar_info->op_mode = le16_to_cpu(nic_info->op_mode);
|
|
npar_info->phys_port = le16_to_cpu(nic_info->phys_port);
|
|
npar_info->switch_mode = le16_to_cpu(nic_info->switch_mode);
|
|
npar_info->max_tx_ques = le16_to_cpu(nic_info->max_tx_ques);
|
|
npar_info->max_rx_ques = le16_to_cpu(nic_info->max_rx_ques);
|
|
npar_info->min_tx_bw = le16_to_cpu(nic_info->min_tx_bw);
|
|
npar_info->max_tx_bw = le16_to_cpu(nic_info->max_tx_bw);
|
|
npar_info->capabilities = le32_to_cpu(nic_info->capabilities);
|
|
npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu);
|
|
|
|
dev_info(&adapter->pdev->dev,
|
|
"phy port: %d switch_mode: %d,\n"
|
|
"\tmax_tx_q: %d max_rx_q: %d min_tx_bw: 0x%x,\n"
|
|
"\tmax_tx_bw: 0x%x max_mtu:0x%x, capabilities: 0x%x\n",
|
|
npar_info->phys_port, npar_info->switch_mode,
|
|
npar_info->max_tx_ques, npar_info->max_rx_ques,
|
|
npar_info->min_tx_bw, npar_info->max_tx_bw,
|
|
npar_info->max_mtu, npar_info->capabilities);
|
|
} else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get nic info%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
|
|
dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
|
|
nic_dma_t);
|
|
return err;
|
|
}
|
|
|
|
/* Configure a NIC partition */
|
|
int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic)
|
|
{
|
|
int err = -EIO;
|
|
dma_addr_t nic_dma_t;
|
|
void *nic_info_addr;
|
|
struct qlcnic_cmd_args cmd;
|
|
struct qlcnic_info *nic_info;
|
|
size_t nic_size = sizeof(struct qlcnic_info);
|
|
|
|
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
|
|
return err;
|
|
|
|
nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size,
|
|
&nic_dma_t, GFP_KERNEL);
|
|
if (!nic_info_addr)
|
|
return -ENOMEM;
|
|
|
|
memset(nic_info_addr, 0, nic_size);
|
|
nic_info = nic_info_addr;
|
|
|
|
nic_info->pci_func = cpu_to_le16(nic->pci_func);
|
|
nic_info->op_mode = cpu_to_le16(nic->op_mode);
|
|
nic_info->phys_port = cpu_to_le16(nic->phys_port);
|
|
nic_info->switch_mode = cpu_to_le16(nic->switch_mode);
|
|
nic_info->capabilities = cpu_to_le32(nic->capabilities);
|
|
nic_info->max_mac_filters = nic->max_mac_filters;
|
|
nic_info->max_tx_ques = cpu_to_le16(nic->max_tx_ques);
|
|
nic_info->max_rx_ques = cpu_to_le16(nic->max_rx_ques);
|
|
nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw);
|
|
nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw);
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_SET_NIC_INFO;
|
|
cmd.req.arg1 = MSD(nic_dma_t);
|
|
cmd.req.arg2 = LSD(nic_dma_t);
|
|
cmd.req.arg3 = ((nic->pci_func << 16) | nic_size);
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (err != QLCNIC_RCODE_SUCCESS) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to set nic info%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
|
|
dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
|
|
nic_dma_t);
|
|
return err;
|
|
}
|
|
|
|
/* Get PCI Info of a partition */
|
|
int qlcnic_get_pci_info(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_pci_info *pci_info)
|
|
{
|
|
int err = 0, i;
|
|
struct qlcnic_cmd_args cmd;
|
|
dma_addr_t pci_info_dma_t;
|
|
struct qlcnic_pci_info *npar;
|
|
void *pci_info_addr;
|
|
size_t npar_size = sizeof(struct qlcnic_pci_info);
|
|
size_t pci_size = npar_size * QLCNIC_MAX_PCI_FUNC;
|
|
|
|
pci_info_addr = dma_alloc_coherent(&adapter->pdev->dev, pci_size,
|
|
&pci_info_dma_t, GFP_KERNEL);
|
|
if (!pci_info_addr)
|
|
return -ENOMEM;
|
|
memset(pci_info_addr, 0, pci_size);
|
|
|
|
npar = pci_info_addr;
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_GET_PCI_INFO;
|
|
cmd.req.arg1 = MSD(pci_info_dma_t);
|
|
cmd.req.arg2 = LSD(pci_info_dma_t);
|
|
cmd.req.arg3 = pci_size;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS) {
|
|
for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++, npar++, pci_info++) {
|
|
pci_info->id = le16_to_cpu(npar->id);
|
|
pci_info->active = le16_to_cpu(npar->active);
|
|
pci_info->type = le16_to_cpu(npar->type);
|
|
pci_info->default_port =
|
|
le16_to_cpu(npar->default_port);
|
|
pci_info->tx_min_bw =
|
|
le16_to_cpu(npar->tx_min_bw);
|
|
pci_info->tx_max_bw =
|
|
le16_to_cpu(npar->tx_max_bw);
|
|
memcpy(pci_info->mac, npar->mac, ETH_ALEN);
|
|
}
|
|
} else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get PCI Info%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
|
|
dma_free_coherent(&adapter->pdev->dev, pci_size, pci_info_addr,
|
|
pci_info_dma_t);
|
|
return err;
|
|
}
|
|
|
|
/* Configure eSwitch for port mirroring */
|
|
int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
|
|
u8 enable_mirroring, u8 pci_func)
|
|
{
|
|
int err = -EIO;
|
|
u32 arg1;
|
|
struct qlcnic_cmd_args cmd;
|
|
|
|
if (adapter->op_mode != QLCNIC_MGMT_FUNC ||
|
|
!(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE))
|
|
return err;
|
|
|
|
arg1 = id | (enable_mirroring ? BIT_4 : 0);
|
|
arg1 |= pci_func << 8;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_SET_PORTMIRRORING;
|
|
cmd.req.arg1 = arg1;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (err != QLCNIC_RCODE_SUCCESS) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to configure port mirroring%d on eswitch:%d\n",
|
|
pci_func, id);
|
|
} else {
|
|
dev_info(&adapter->pdev->dev,
|
|
"Configured eSwitch %d for port mirroring:%d\n",
|
|
id, pci_func);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
|
|
const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {
|
|
|
|
size_t stats_size = sizeof(struct __qlcnic_esw_statistics);
|
|
struct __qlcnic_esw_statistics *stats;
|
|
dma_addr_t stats_dma_t;
|
|
void *stats_addr;
|
|
u32 arg1;
|
|
struct qlcnic_cmd_args cmd;
|
|
int err;
|
|
|
|
if (esw_stats == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (adapter->op_mode != QLCNIC_MGMT_FUNC &&
|
|
func != adapter->ahw->pci_func) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Not privilege to query stats for func=%d", func);
|
|
return -EIO;
|
|
}
|
|
|
|
stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size,
|
|
&stats_dma_t, GFP_KERNEL);
|
|
if (!stats_addr) {
|
|
dev_err(&adapter->pdev->dev, "Unable to allocate memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
memset(stats_addr, 0, stats_size);
|
|
|
|
arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;
|
|
arg1 |= rx_tx << 15 | stats_size << 16;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_STATS;
|
|
cmd.req.arg1 = arg1;
|
|
cmd.req.arg2 = MSD(stats_dma_t);
|
|
cmd.req.arg3 = LSD(stats_dma_t);
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (!err) {
|
|
stats = stats_addr;
|
|
esw_stats->context_id = le16_to_cpu(stats->context_id);
|
|
esw_stats->version = le16_to_cpu(stats->version);
|
|
esw_stats->size = le16_to_cpu(stats->size);
|
|
esw_stats->multicast_frames =
|
|
le64_to_cpu(stats->multicast_frames);
|
|
esw_stats->broadcast_frames =
|
|
le64_to_cpu(stats->broadcast_frames);
|
|
esw_stats->unicast_frames = le64_to_cpu(stats->unicast_frames);
|
|
esw_stats->dropped_frames = le64_to_cpu(stats->dropped_frames);
|
|
esw_stats->local_frames = le64_to_cpu(stats->local_frames);
|
|
esw_stats->errors = le64_to_cpu(stats->errors);
|
|
esw_stats->numbytes = le64_to_cpu(stats->numbytes);
|
|
}
|
|
|
|
dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
|
|
stats_dma_t);
|
|
return err;
|
|
}
|
|
|
|
/* This routine will retrieve the MAC statistics from firmware */
|
|
int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_mac_statistics *mac_stats)
|
|
{
|
|
struct qlcnic_mac_statistics *stats;
|
|
struct qlcnic_cmd_args cmd;
|
|
size_t stats_size = sizeof(struct qlcnic_mac_statistics);
|
|
dma_addr_t stats_dma_t;
|
|
void *stats_addr;
|
|
int err;
|
|
|
|
stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size,
|
|
&stats_dma_t, GFP_KERNEL);
|
|
if (!stats_addr) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"%s: Unable to allocate memory.\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
memset(stats_addr, 0, stats_size);
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_GET_MAC_STATS;
|
|
cmd.req.arg1 = stats_size << 16;
|
|
cmd.req.arg2 = MSD(stats_dma_t);
|
|
cmd.req.arg3 = LSD(stats_dma_t);
|
|
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (!err) {
|
|
stats = stats_addr;
|
|
mac_stats->mac_tx_frames = le64_to_cpu(stats->mac_tx_frames);
|
|
mac_stats->mac_tx_bytes = le64_to_cpu(stats->mac_tx_bytes);
|
|
mac_stats->mac_tx_mcast_pkts =
|
|
le64_to_cpu(stats->mac_tx_mcast_pkts);
|
|
mac_stats->mac_tx_bcast_pkts =
|
|
le64_to_cpu(stats->mac_tx_bcast_pkts);
|
|
mac_stats->mac_rx_frames = le64_to_cpu(stats->mac_rx_frames);
|
|
mac_stats->mac_rx_bytes = le64_to_cpu(stats->mac_rx_bytes);
|
|
mac_stats->mac_rx_mcast_pkts =
|
|
le64_to_cpu(stats->mac_rx_mcast_pkts);
|
|
mac_stats->mac_rx_length_error =
|
|
le64_to_cpu(stats->mac_rx_length_error);
|
|
mac_stats->mac_rx_length_small =
|
|
le64_to_cpu(stats->mac_rx_length_small);
|
|
mac_stats->mac_rx_length_large =
|
|
le64_to_cpu(stats->mac_rx_length_large);
|
|
mac_stats->mac_rx_jabber = le64_to_cpu(stats->mac_rx_jabber);
|
|
mac_stats->mac_rx_dropped = le64_to_cpu(stats->mac_rx_dropped);
|
|
mac_stats->mac_rx_crc_error = le64_to_cpu(stats->mac_rx_crc_error);
|
|
} else {
|
|
dev_info(&adapter->pdev->dev,
|
|
"%s: Get mac stats failed =%d.\n", __func__, err);
|
|
}
|
|
|
|
dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
|
|
stats_dma_t);
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_get_eswitch_stats(struct qlcnic_adapter *adapter, const u8 eswitch,
|
|
const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {
|
|
|
|
struct __qlcnic_esw_statistics port_stats;
|
|
u8 i;
|
|
int ret = -EIO;
|
|
|
|
if (esw_stats == NULL)
|
|
return -ENOMEM;
|
|
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
|
|
return -EIO;
|
|
if (adapter->npars == NULL)
|
|
return -EIO;
|
|
|
|
memset(esw_stats, 0, sizeof(u64));
|
|
esw_stats->unicast_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->multicast_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->broadcast_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->dropped_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->errors = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->local_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->numbytes = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->context_id = eswitch;
|
|
|
|
for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
|
|
if (adapter->npars[i].phy_port != eswitch)
|
|
continue;
|
|
|
|
memset(&port_stats, 0, sizeof(struct __qlcnic_esw_statistics));
|
|
if (qlcnic_get_port_stats(adapter, i, rx_tx, &port_stats))
|
|
continue;
|
|
|
|
esw_stats->size = port_stats.size;
|
|
esw_stats->version = port_stats.version;
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->unicast_frames,
|
|
port_stats.unicast_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->multicast_frames,
|
|
port_stats.multicast_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->broadcast_frames,
|
|
port_stats.broadcast_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->dropped_frames,
|
|
port_stats.dropped_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->errors,
|
|
port_stats.errors);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->local_frames,
|
|
port_stats.local_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->numbytes,
|
|
port_stats.numbytes);
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
|
|
const u8 port, const u8 rx_tx)
|
|
{
|
|
|
|
u32 arg1;
|
|
struct qlcnic_cmd_args cmd;
|
|
|
|
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
|
|
return -EIO;
|
|
|
|
if (func_esw == QLCNIC_STATS_PORT) {
|
|
if (port >= QLCNIC_MAX_PCI_FUNC)
|
|
goto err_ret;
|
|
} else if (func_esw == QLCNIC_STATS_ESWITCH) {
|
|
if (port >= QLCNIC_NIU_MAX_XG_PORTS)
|
|
goto err_ret;
|
|
} else {
|
|
goto err_ret;
|
|
}
|
|
|
|
if (rx_tx > QLCNIC_QUERY_TX_COUNTER)
|
|
goto err_ret;
|
|
|
|
arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12;
|
|
arg1 |= BIT_14 | rx_tx << 15;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_STATS;
|
|
cmd.req.arg1 = arg1;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
return cmd.rsp.cmd;
|
|
|
|
err_ret:
|
|
dev_err(&adapter->pdev->dev, "Invalid argument func_esw=%d port=%d"
|
|
"rx_ctx=%d\n", func_esw, port, rx_tx);
|
|
return -EIO;
|
|
}
|
|
|
|
static int
|
|
__qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
|
|
u32 *arg1, u32 *arg2)
|
|
{
|
|
int err = -EIO;
|
|
struct qlcnic_cmd_args cmd;
|
|
u8 pci_func;
|
|
pci_func = (*arg1 >> 8);
|
|
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG;
|
|
cmd.req.arg1 = *arg1;
|
|
cmd.rsp.arg1 = cmd.rsp.arg2 = 1;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
*arg1 = cmd.rsp.arg1;
|
|
*arg2 = cmd.rsp.arg2;
|
|
err = cmd.rsp.cmd;
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS) {
|
|
dev_info(&adapter->pdev->dev,
|
|
"eSwitch port config for pci func %d\n", pci_func);
|
|
} else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get eswitch port config for pci func %d\n",
|
|
pci_func);
|
|
}
|
|
return err;
|
|
}
|
|
/* Configure eSwitch port
|
|
op_mode = 0 for setting default port behavior
|
|
op_mode = 1 for setting vlan id
|
|
op_mode = 2 for deleting vlan id
|
|
op_type = 0 for vlan_id
|
|
op_type = 1 for port vlan_id
|
|
*/
|
|
int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_esw_func_cfg *esw_cfg)
|
|
{
|
|
int err = -EIO;
|
|
u32 arg1, arg2 = 0;
|
|
struct qlcnic_cmd_args cmd;
|
|
u8 pci_func;
|
|
|
|
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
|
|
return err;
|
|
pci_func = esw_cfg->pci_func;
|
|
arg1 = (adapter->npars[pci_func].phy_port & BIT_0);
|
|
arg1 |= (pci_func << 8);
|
|
|
|
if (__qlcnic_get_eswitch_port_config(adapter, &arg1, &arg2))
|
|
return err;
|
|
arg1 &= ~(0x0ff << 8);
|
|
arg1 |= (pci_func << 8);
|
|
arg1 &= ~(BIT_2 | BIT_3);
|
|
switch (esw_cfg->op_mode) {
|
|
case QLCNIC_PORT_DEFAULTS:
|
|
arg1 |= (BIT_4 | BIT_6 | BIT_7);
|
|
arg2 |= (BIT_0 | BIT_1);
|
|
if (adapter->capabilities & QLCNIC_FW_CAPABILITY_TSO)
|
|
arg2 |= (BIT_2 | BIT_3);
|
|
if (!(esw_cfg->discard_tagged))
|
|
arg1 &= ~BIT_4;
|
|
if (!(esw_cfg->promisc_mode))
|
|
arg1 &= ~BIT_6;
|
|
if (!(esw_cfg->mac_override))
|
|
arg1 &= ~BIT_7;
|
|
if (!(esw_cfg->mac_anti_spoof))
|
|
arg2 &= ~BIT_0;
|
|
if (!(esw_cfg->offload_flags & BIT_0))
|
|
arg2 &= ~(BIT_1 | BIT_2 | BIT_3);
|
|
if (!(esw_cfg->offload_flags & BIT_1))
|
|
arg2 &= ~BIT_2;
|
|
if (!(esw_cfg->offload_flags & BIT_2))
|
|
arg2 &= ~BIT_3;
|
|
break;
|
|
case QLCNIC_ADD_VLAN:
|
|
arg1 |= (BIT_2 | BIT_5);
|
|
arg1 |= (esw_cfg->vlan_id << 16);
|
|
break;
|
|
case QLCNIC_DEL_VLAN:
|
|
arg1 |= (BIT_3 | BIT_5);
|
|
arg1 &= ~(0x0ffff << 16);
|
|
break;
|
|
default:
|
|
return err;
|
|
}
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.req.cmd = QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH;
|
|
cmd.req.arg1 = arg1;
|
|
cmd.req.arg2 = arg2;
|
|
qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
err = cmd.rsp.cmd;
|
|
if (err != QLCNIC_RCODE_SUCCESS) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to configure eswitch pci func %d\n", pci_func);
|
|
} else {
|
|
dev_info(&adapter->pdev->dev,
|
|
"Configured eSwitch for pci func %d\n", pci_func);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_esw_func_cfg *esw_cfg)
|
|
{
|
|
u32 arg1, arg2;
|
|
u8 phy_port;
|
|
if (adapter->op_mode == QLCNIC_MGMT_FUNC)
|
|
phy_port = adapter->npars[esw_cfg->pci_func].phy_port;
|
|
else
|
|
phy_port = adapter->physical_port;
|
|
arg1 = phy_port;
|
|
arg1 |= (esw_cfg->pci_func << 8);
|
|
if (__qlcnic_get_eswitch_port_config(adapter, &arg1, &arg2))
|
|
return -EIO;
|
|
|
|
esw_cfg->discard_tagged = !!(arg1 & BIT_4);
|
|
esw_cfg->host_vlan_tag = !!(arg1 & BIT_5);
|
|
esw_cfg->promisc_mode = !!(arg1 & BIT_6);
|
|
esw_cfg->mac_override = !!(arg1 & BIT_7);
|
|
esw_cfg->vlan_id = LSW(arg1 >> 16);
|
|
esw_cfg->mac_anti_spoof = (arg2 & 0x1);
|
|
esw_cfg->offload_flags = ((arg2 >> 1) & 0x7);
|
|
|
|
return 0;
|
|
}
|