Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_BNXT) += bnxt_en.o
bnxt_en-y := bnxt.o bnxt_hwrm.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_ptp.o bnxt_vfr.o bnxt_devlink.o bnxt_dim.o bnxt_coredump.o bnxt_gso.o
bnxt_en-$(CONFIG_BNXT_FLOWER_OFFLOAD) += bnxt_tc.o
bnxt_en-$(CONFIG_DEBUG_FS) += bnxt_debugfs.o
bnxt_en-$(CONFIG_BNXT_HWMON) += bnxt_hwmon.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,677 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2021 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_coredump.h"
static const u16 bnxt_bstore_to_seg_id[] = {
[BNXT_CTX_QP] = BNXT_CTX_MEM_SEG_QP,
[BNXT_CTX_SRQ] = BNXT_CTX_MEM_SEG_SRQ,
[BNXT_CTX_CQ] = BNXT_CTX_MEM_SEG_CQ,
[BNXT_CTX_VNIC] = BNXT_CTX_MEM_SEG_VNIC,
[BNXT_CTX_STAT] = BNXT_CTX_MEM_SEG_STAT,
[BNXT_CTX_STQM] = BNXT_CTX_MEM_SEG_STQM,
[BNXT_CTX_FTQM] = BNXT_CTX_MEM_SEG_FTQM,
[BNXT_CTX_MRAV] = BNXT_CTX_MEM_SEG_MRAV,
[BNXT_CTX_TIM] = BNXT_CTX_MEM_SEG_TIM,
[BNXT_CTX_SRT] = BNXT_CTX_MEM_SEG_SRT,
[BNXT_CTX_SRT2] = BNXT_CTX_MEM_SEG_SRT2,
[BNXT_CTX_CRT] = BNXT_CTX_MEM_SEG_CRT,
[BNXT_CTX_CRT2] = BNXT_CTX_MEM_SEG_CRT2,
[BNXT_CTX_RIGP0] = BNXT_CTX_MEM_SEG_RIGP0,
[BNXT_CTX_L2HWRM] = BNXT_CTX_MEM_SEG_L2HWRM,
[BNXT_CTX_REHWRM] = BNXT_CTX_MEM_SEG_REHWRM,
[BNXT_CTX_CA0] = BNXT_CTX_MEM_SEG_CA0,
[BNXT_CTX_CA1] = BNXT_CTX_MEM_SEG_CA1,
[BNXT_CTX_CA2] = BNXT_CTX_MEM_SEG_CA2,
[BNXT_CTX_RIGP1] = BNXT_CTX_MEM_SEG_RIGP1,
[BNXT_CTX_KONG] = BNXT_CTX_MEM_SEG_KONG,
[BNXT_CTX_QPC] = BNXT_CTX_MEM_SEG_QPC,
};
static int bnxt_dbg_hwrm_log_buffer_flush(struct bnxt *bp, u16 type, u32 flags,
u32 *offset)
{
struct hwrm_dbg_log_buffer_flush_output *resp;
struct hwrm_dbg_log_buffer_flush_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_DBG_LOG_BUFFER_FLUSH);
if (rc)
return rc;
req->flags = cpu_to_le32(flags);
req->type = cpu_to_le16(type);
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
if (!rc)
*offset = le32_to_cpu(resp->current_buffer_offset);
hwrm_req_drop(bp, req);
return rc;
}
static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg,
struct bnxt_hwrm_dbg_dma_info *info)
{
struct hwrm_dbg_cmn_input *cmn_req = msg;
__le16 *seq_ptr = msg + info->seq_off;
struct hwrm_dbg_cmn_output *cmn_resp;
u16 seq = 0, len, segs_off;
dma_addr_t dma_handle;
void *dma_buf, *resp;
int rc, off = 0;
dma_buf = hwrm_req_dma_slice(bp, msg, info->dma_len, &dma_handle);
if (!dma_buf) {
hwrm_req_drop(bp, msg);
return -ENOMEM;
}
hwrm_req_timeout(bp, msg, bp->hwrm_cmd_max_timeout);
cmn_resp = hwrm_req_hold(bp, msg);
resp = cmn_resp;
segs_off = offsetof(struct hwrm_dbg_coredump_list_output,
total_segments);
cmn_req->host_dest_addr = cpu_to_le64(dma_handle);
cmn_req->host_buf_len = cpu_to_le32(info->dma_len);
while (1) {
*seq_ptr = cpu_to_le16(seq);
rc = hwrm_req_send(bp, msg);
if (rc)
break;
len = le16_to_cpu(*((__le16 *)(resp + info->data_len_off)));
if (!seq &&
cmn_req->req_type == cpu_to_le16(HWRM_DBG_COREDUMP_LIST)) {
info->segs = le16_to_cpu(*((__le16 *)(resp +
segs_off)));
if (!info->segs) {
rc = -EIO;
break;
}
info->dest_buf_size = info->segs *
sizeof(struct coredump_segment_record);
info->dest_buf = kmalloc(info->dest_buf_size,
GFP_KERNEL);
if (!info->dest_buf) {
rc = -ENOMEM;
break;
}
}
if (cmn_req->req_type ==
cpu_to_le16(HWRM_DBG_COREDUMP_RETRIEVE))
info->dest_buf_size += len;
if (info->dest_buf) {
if ((info->seg_start + off + len) <=
BNXT_COREDUMP_BUF_LEN(info->buf_len)) {
u16 copylen = min_t(u16, len,
info->dest_buf_size - off);
memcpy(info->dest_buf + off, dma_buf, copylen);
if (copylen < len)
break;
} else {
rc = -ENOBUFS;
if (cmn_req->req_type ==
cpu_to_le16(HWRM_DBG_COREDUMP_LIST)) {
kfree(info->dest_buf);
info->dest_buf = NULL;
}
break;
}
}
if (!(cmn_resp->flags & HWRM_DBG_CMN_FLAGS_MORE))
break;
seq++;
off += len;
}
hwrm_req_drop(bp, msg);
return rc;
}
static int bnxt_hwrm_dbg_coredump_list(struct bnxt *bp,
struct bnxt_coredump *coredump)
{
struct bnxt_hwrm_dbg_dma_info info = {NULL};
struct hwrm_dbg_coredump_list_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_LIST);
if (rc)
return rc;
info.dma_len = COREDUMP_LIST_BUF_LEN;
info.seq_off = offsetof(struct hwrm_dbg_coredump_list_input, seq_no);
info.data_len_off = offsetof(struct hwrm_dbg_coredump_list_output,
data_len);
rc = bnxt_hwrm_dbg_dma_data(bp, req, &info);
if (!rc) {
coredump->data = info.dest_buf;
coredump->data_size = info.dest_buf_size;
coredump->total_segs = info.segs;
}
return rc;
}
static int bnxt_hwrm_dbg_coredump_initiate(struct bnxt *bp, u16 dump_type,
u16 component_id, u16 segment_id)
{
struct hwrm_dbg_coredump_initiate_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_INITIATE);
if (rc)
return rc;
hwrm_req_timeout(bp, req, bp->hwrm_cmd_max_timeout);
req->component_id = cpu_to_le16(component_id);
req->segment_id = cpu_to_le16(segment_id);
if (dump_type == BNXT_DUMP_LIVE_WITH_CTX_L1_CACHE)
req->seg_flags = DBG_COREDUMP_INITIATE_REQ_SEG_FLAGS_COLLECT_CTX_L1_CACHE;
return hwrm_req_send(bp, req);
}
static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id,
u16 segment_id, u32 *seg_len,
void *buf, u32 buf_len, u32 offset)
{
struct hwrm_dbg_coredump_retrieve_input *req;
struct bnxt_hwrm_dbg_dma_info info = {NULL};
int rc;
rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_RETRIEVE);
if (rc)
return rc;
req->component_id = cpu_to_le16(component_id);
req->segment_id = cpu_to_le16(segment_id);
info.dma_len = COREDUMP_RETRIEVE_BUF_LEN;
info.seq_off = offsetof(struct hwrm_dbg_coredump_retrieve_input,
seq_no);
info.data_len_off = offsetof(struct hwrm_dbg_coredump_retrieve_output,
data_len);
if (buf) {
info.dest_buf = buf + offset;
info.buf_len = buf_len;
info.seg_start = offset;
}
rc = bnxt_hwrm_dbg_dma_data(bp, req, &info);
if (!rc)
*seg_len = info.dest_buf_size;
return rc;
}
void
bnxt_fill_coredump_seg_hdr(struct bnxt *bp,
struct bnxt_coredump_segment_hdr *seg_hdr,
struct coredump_segment_record *seg_rec, u32 seg_len,
int status, u32 duration, u32 instance, u32 comp_id,
u32 seg_id)
{
memset(seg_hdr, 0, sizeof(*seg_hdr));
memcpy(seg_hdr->signature, "sEgM", 4);
if (seg_rec) {
seg_hdr->component_id = (__force __le32)seg_rec->component_id;
seg_hdr->segment_id = (__force __le32)seg_rec->segment_id;
seg_hdr->low_version = seg_rec->version_low;
seg_hdr->high_version = seg_rec->version_hi;
seg_hdr->flags = cpu_to_le32(seg_rec->compress_flags);
} else {
seg_hdr->component_id = cpu_to_le32(comp_id);
seg_hdr->segment_id = cpu_to_le32(seg_id);
}
seg_hdr->function_id = cpu_to_le16(bp->pdev->devfn);
seg_hdr->length = cpu_to_le32(seg_len);
seg_hdr->status = cpu_to_le32(status);
seg_hdr->duration = cpu_to_le32(duration);
seg_hdr->data_offset = cpu_to_le32(sizeof(*seg_hdr));
seg_hdr->instance = cpu_to_le32(instance);
}
static void bnxt_fill_cmdline(struct bnxt_coredump_record *record)
{
struct mm_struct *mm = current->mm;
int i, len, last = 0;
if (mm) {
len = min_t(int, mm->arg_end - mm->arg_start,
sizeof(record->commandline) - 1);
if (len && !copy_from_user(record->commandline,
(char __user *)mm->arg_start, len)) {
for (i = 0; i < len; i++) {
if (record->commandline[i])
last = i;
else
record->commandline[i] = ' ';
}
record->commandline[last + 1] = 0;
return;
}
}
strscpy(record->commandline, current->comm, TASK_COMM_LEN);
}
static void
bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record,
time64_t start, s16 start_utc, u16 total_segs,
int status)
{
time64_t end = ktime_get_real_seconds();
u32 os_ver_major = 0, os_ver_minor = 0;
struct tm tm;
time64_to_tm(start, 0, &tm);
memset(record, 0, sizeof(*record));
memcpy(record->signature, "cOrE", 4);
record->flags = 0;
record->low_version = 0;
record->high_version = 1;
record->asic_state = 0;
strscpy(record->system_name, utsname()->nodename,
sizeof(record->system_name));
record->year = cpu_to_le16(tm.tm_year + 1900);
record->month = cpu_to_le16(tm.tm_mon + 1);
record->day = cpu_to_le16(tm.tm_mday);
record->hour = cpu_to_le16(tm.tm_hour);
record->minute = cpu_to_le16(tm.tm_min);
record->second = cpu_to_le16(tm.tm_sec);
record->utc_bias = cpu_to_le16(start_utc);
bnxt_fill_cmdline(record);
record->total_segments = cpu_to_le32(total_segs);
if (sscanf(utsname()->release, "%u.%u", &os_ver_major, &os_ver_minor) != 2)
netdev_warn(bp->dev, "Unknown OS release in coredump\n");
record->os_ver_major = cpu_to_le32(os_ver_major);
record->os_ver_minor = cpu_to_le32(os_ver_minor);
strscpy(record->os_name, utsname()->sysname, sizeof(record->os_name));
time64_to_tm(end, 0, &tm);
record->end_year = cpu_to_le16(tm.tm_year + 1900);
record->end_month = cpu_to_le16(tm.tm_mon + 1);
record->end_day = cpu_to_le16(tm.tm_mday);
record->end_hour = cpu_to_le16(tm.tm_hour);
record->end_minute = cpu_to_le16(tm.tm_min);
record->end_second = cpu_to_le16(tm.tm_sec);
record->end_utc_bias = cpu_to_le16(sys_tz.tz_minuteswest * 60);
record->asic_id1 = cpu_to_le32(bp->chip_num << 16 |
bp->ver_resp.chip_rev << 8 |
bp->ver_resp.chip_metal);
record->asic_id2 = 0;
record->coredump_status = cpu_to_le32(status);
record->ioctl_low_version = 0;
record->ioctl_high_version = 0;
}
static void bnxt_fill_drv_seg_record(struct bnxt *bp,
struct bnxt_driver_segment_record *record,
struct bnxt_ctx_mem_type *ctxm, u16 type)
{
struct bnxt_bs_trace_info *bs_trace = &bp->bs_trace[type];
u32 offset = 0;
int rc = 0;
record->max_entries = cpu_to_le32(ctxm->max_entries);
record->entry_size = cpu_to_le32(ctxm->entry_size);
rc = bnxt_dbg_hwrm_log_buffer_flush(bp, type, 0, &offset);
if (rc)
return;
bnxt_bs_trace_check_wrap(bs_trace, offset);
record->offset = cpu_to_le32(bs_trace->last_offset);
record->wrapped = bs_trace->wrapped;
}
static u32 bnxt_get_ctx_coredump(struct bnxt *bp, void *buf, u32 offset,
u32 *segs)
{
struct bnxt_driver_segment_record record = {};
struct bnxt_coredump_segment_hdr seg_hdr;
struct bnxt_ctx_mem_info *ctx = bp->ctx;
u32 comp_id = BNXT_DRV_COMP_ID;
void *data = NULL;
size_t len = 0;
u16 type;
*segs = 0;
if (!ctx)
return 0;
if (buf)
buf += offset;
for (type = 0; type < BNXT_CTX_V2_MAX; type++) {
struct bnxt_ctx_mem_type *ctxm = &ctx->ctx_arr[type];
bool trace = bnxt_bs_trace_avail(bp, type);
u32 seg_id = bnxt_bstore_to_seg_id[type];
size_t seg_len, extra_hlen = 0;
if (!ctxm->mem_valid || !seg_id)
continue;
if (trace) {
extra_hlen = BNXT_SEG_RCD_LEN;
if (buf) {
u16 trace_type = bnxt_bstore_to_trace[type];
bnxt_fill_drv_seg_record(bp, &record, ctxm,
trace_type);
}
}
if (buf)
data = buf + BNXT_SEG_HDR_LEN + extra_hlen;
seg_len = bnxt_copy_ctx_mem(bp, ctxm, data, 0) + extra_hlen;
if (buf) {
bnxt_fill_coredump_seg_hdr(bp, &seg_hdr, NULL, seg_len,
0, 0, 0, comp_id, seg_id);
memcpy(buf, &seg_hdr, BNXT_SEG_HDR_LEN);
buf += BNXT_SEG_HDR_LEN;
if (trace)
memcpy(buf, &record, BNXT_SEG_RCD_LEN);
buf += seg_len;
}
len += BNXT_SEG_HDR_LEN + seg_len;
*segs += 1;
}
return len;
}
static int __bnxt_get_coredump(struct bnxt *bp, u16 dump_type, void *buf,
u32 *dump_len)
{
u32 ver_get_resp_len = sizeof(struct hwrm_ver_get_output);
u32 offset = 0, seg_hdr_len, seg_record_len, buf_len = 0;
struct coredump_segment_record *seg_record = NULL;
struct bnxt_coredump_segment_hdr seg_hdr;
struct bnxt_coredump coredump = {NULL};
time64_t start_time;
u16 start_utc;
int rc = 0, i;
if (buf)
buf_len = *dump_len;
start_time = ktime_get_real_seconds();
start_utc = sys_tz.tz_minuteswest * 60;
seg_hdr_len = sizeof(seg_hdr);
/* First segment should be hwrm_ver_get response.
* For hwrm_ver_get response Component id = 2 and Segment id = 0.
*/
*dump_len = seg_hdr_len + ver_get_resp_len;
if (buf) {
bnxt_fill_coredump_seg_hdr(bp, &seg_hdr, NULL, ver_get_resp_len,
0, 0, 0, BNXT_VER_GET_COMP_ID, 0);
memcpy(buf + offset, &seg_hdr, seg_hdr_len);
offset += seg_hdr_len;
memcpy(buf + offset, &bp->ver_resp, ver_get_resp_len);
offset += ver_get_resp_len;
}
if (dump_type == BNXT_DUMP_DRIVER) {
u32 drv_len, segs = 0;
drv_len = bnxt_get_ctx_coredump(bp, buf, offset, &segs);
*dump_len += drv_len;
offset += drv_len;
if (buf)
coredump.total_segs += segs;
goto err;
}
seg_record_len = sizeof(*seg_record);
rc = bnxt_hwrm_dbg_coredump_list(bp, &coredump);
if (rc) {
netdev_err(bp->dev, "Failed to get coredump segment list\n");
goto err;
}
*dump_len += seg_hdr_len * coredump.total_segs;
seg_record = (struct coredump_segment_record *)coredump.data;
seg_record_len = sizeof(*seg_record);
for (i = 0; i < coredump.total_segs; i++) {
u16 comp_id = le16_to_cpu(seg_record->component_id);
u16 seg_id = le16_to_cpu(seg_record->segment_id);
u32 duration = 0, seg_len = 0;
unsigned long start, end;
if (buf && ((offset + seg_hdr_len) >
BNXT_COREDUMP_BUF_LEN(buf_len))) {
rc = -ENOBUFS;
goto err;
}
start = jiffies;
rc = bnxt_hwrm_dbg_coredump_initiate(bp, dump_type, comp_id,
seg_id);
if (rc) {
netdev_err(bp->dev,
"Failed to initiate coredump for seg = %d\n",
seg_record->segment_id);
goto next_seg;
}
/* Write segment data into the buffer */
rc = bnxt_hwrm_dbg_coredump_retrieve(bp, comp_id, seg_id,
&seg_len, buf, buf_len,
offset + seg_hdr_len);
if (rc && rc == -ENOBUFS)
goto err;
else if (rc)
netdev_err(bp->dev,
"Failed to retrieve coredump for seg = %d\n",
seg_record->segment_id);
next_seg:
end = jiffies;
duration = jiffies_to_msecs(end - start);
bnxt_fill_coredump_seg_hdr(bp, &seg_hdr, seg_record, seg_len,
rc, duration, 0, 0, 0);
if (buf) {
/* Write segment header into the buffer */
memcpy(buf + offset, &seg_hdr, seg_hdr_len);
offset += seg_hdr_len + seg_len;
}
*dump_len += seg_len;
seg_record =
(struct coredump_segment_record *)((u8 *)seg_record +
seg_record_len);
}
err:
if (buf)
bnxt_fill_coredump_record(bp, buf + offset, start_time,
start_utc, coredump.total_segs + 1,
rc);
kfree(coredump.data);
if (!rc) {
*dump_len += sizeof(struct bnxt_coredump_record);
/* The actual coredump length can be smaller than the FW
* reported length earlier. Use the ethtool provided length.
*/
if (buf_len)
*dump_len = buf_len;
} else if (rc == -ENOBUFS) {
netdev_err(bp->dev, "Firmware returned large coredump buffer\n");
}
return rc;
}
static u32 bnxt_copy_crash_data(struct bnxt_ring_mem_info *rmem, void *buf,
u32 dump_len)
{
u32 data_copied = 0;
u32 data_len;
int i;
for (i = 0; i < rmem->nr_pages; i++) {
data_len = rmem->page_size;
if (data_copied + data_len > dump_len)
data_len = dump_len - data_copied;
memcpy(buf + data_copied, rmem->pg_arr[i], data_len);
data_copied += data_len;
if (data_copied >= dump_len)
break;
}
return data_copied;
}
static int bnxt_copy_crash_dump(struct bnxt *bp, void *buf, u32 dump_len)
{
struct bnxt_ring_mem_info *rmem;
u32 offset = 0;
if (!bp->fw_crash_mem)
return -ENOENT;
rmem = &bp->fw_crash_mem->ring_mem;
if (rmem->depth > 1) {
int i;
for (i = 0; i < rmem->nr_pages; i++) {
struct bnxt_ctx_pg_info *pg_tbl;
pg_tbl = bp->fw_crash_mem->ctx_pg_tbl[i];
offset += bnxt_copy_crash_data(&pg_tbl->ring_mem,
buf + offset,
dump_len - offset);
if (offset >= dump_len)
break;
}
} else {
bnxt_copy_crash_data(rmem, buf, dump_len);
}
return 0;
}
static bool bnxt_crash_dump_avail(struct bnxt *bp)
{
u32 sig = 0;
/* First 4 bytes(signature) of crash dump is always non-zero */
bnxt_copy_crash_dump(bp, &sig, sizeof(sig));
return !!sig;
}
int bnxt_get_coredump(struct bnxt *bp, u16 dump_type, void *buf, u32 *dump_len)
{
if (dump_type == BNXT_DUMP_CRASH) {
if (bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_HOST_DDR)
return bnxt_copy_crash_dump(bp, buf, *dump_len);
#ifdef CONFIG_TEE_BNXT_FW
else if (bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_SOC_DDR)
return tee_bnxt_copy_coredump(buf, 0, *dump_len);
#endif
else
return -EOPNOTSUPP;
} else {
return __bnxt_get_coredump(bp, dump_type, buf, dump_len);
}
}
int bnxt_hwrm_get_dump_len(struct bnxt *bp, u16 dump_type, u32 *dump_len)
{
struct hwrm_dbg_qcfg_output *resp;
struct hwrm_dbg_qcfg_input *req;
int rc, hdr_len = 0;
if (!(bp->fw_cap & BNXT_FW_CAP_DBG_QCAPS))
return -EOPNOTSUPP;
if (dump_type == BNXT_DUMP_CRASH &&
!(bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_SOC_DDR ||
(bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_HOST_DDR)))
return -EOPNOTSUPP;
rc = hwrm_req_init(bp, req, HWRM_DBG_QCFG);
if (rc)
return rc;
req->fid = cpu_to_le16(0xffff);
if (dump_type == BNXT_DUMP_CRASH) {
if (bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_SOC_DDR)
req->flags = cpu_to_le16(BNXT_DBG_FL_CR_DUMP_SIZE_SOC);
else
req->flags = cpu_to_le16(BNXT_DBG_FL_CR_DUMP_SIZE_HOST);
}
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
if (rc)
goto get_dump_len_exit;
if (dump_type == BNXT_DUMP_CRASH) {
if (bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_SOC_DDR)
*dump_len = BNXT_CRASH_DUMP_LEN;
else
*dump_len = le32_to_cpu(resp->crashdump_size);
} else {
/* Driver adds coredump header and "HWRM_VER_GET response"
* segment additionally to coredump.
*/
hdr_len = sizeof(struct bnxt_coredump_segment_hdr) +
sizeof(struct hwrm_ver_get_output) +
sizeof(struct bnxt_coredump_record);
*dump_len = le32_to_cpu(resp->coredump_size) + hdr_len;
}
if (*dump_len <= hdr_len)
rc = -EINVAL;
get_dump_len_exit:
hwrm_req_drop(bp, req);
return rc;
}
u32 bnxt_get_coredump_length(struct bnxt *bp, u16 dump_type)
{
u32 len = 0;
if (dump_type == BNXT_DUMP_CRASH &&
bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_HOST_DDR &&
bp->fw_crash_mem) {
if (!bnxt_crash_dump_avail(bp))
return 0;
return bp->fw_crash_len;
}
if (dump_type != BNXT_DUMP_DRIVER) {
if (!bnxt_hwrm_get_dump_len(bp, dump_type, &len))
return len;
}
if (dump_type != BNXT_DUMP_CRASH)
__bnxt_get_coredump(bp, dump_type, NULL, &len);
return len;
}
@@ -0,0 +1,170 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2018 Broadcom Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_COREDUMP_H
#define BNXT_COREDUMP_H
#include <linux/utsname.h>
#include <linux/time.h>
#include <linux/rtc.h>
struct bnxt_coredump_segment_hdr {
__u8 signature[4];
__le32 component_id;
__le32 segment_id;
__le32 flags;
__u8 low_version;
__u8 high_version;
__le16 function_id;
__le32 offset;
__le32 length;
__le32 status;
__le32 duration;
__le32 data_offset;
__le32 instance;
__le32 rsvd[5];
};
struct bnxt_coredump_record {
__u8 signature[4];
__le32 flags;
__u8 low_version;
__u8 high_version;
__u8 asic_state;
__u8 rsvd0[5];
char system_name[32];
__le16 year;
__le16 month;
__le16 day;
__le16 hour;
__le16 minute;
__le16 second;
__le16 utc_bias;
__le16 rsvd1;
char commandline[256];
__le32 total_segments;
__le32 os_ver_major;
__le32 os_ver_minor;
__le32 rsvd2;
char os_name[32];
__le16 end_year;
__le16 end_month;
__le16 end_day;
__le16 end_hour;
__le16 end_minute;
__le16 end_second;
__le16 end_utc_bias;
__le32 asic_id1;
__le32 asic_id2;
__le32 coredump_status;
__u8 ioctl_low_version;
__u8 ioctl_high_version;
__le16 rsvd3[313];
};
struct bnxt_driver_segment_record {
__le32 max_entries;
__le32 entry_size;
__le32 offset;
__u8 wrapped:1;
__u8 unused[3];
};
#define BNXT_VER_GET_COMP_ID 2
#define BNXT_DRV_COMP_ID 0xd
#define BNXT_CTX_MEM_SEG_ID_START 0x200
#define BNXT_CTX_MEM_SEG_QP (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_QP)
#define BNXT_CTX_MEM_SEG_SRQ (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_SRQ)
#define BNXT_CTX_MEM_SEG_CQ (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_CQ)
#define BNXT_CTX_MEM_SEG_VNIC (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_VNIC)
#define BNXT_CTX_MEM_SEG_STAT (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_STAT)
#define BNXT_CTX_MEM_SEG_STQM (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_STQM)
#define BNXT_CTX_MEM_SEG_FTQM (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_FTQM)
#define BNXT_CTX_MEM_SEG_MRAV (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_MRAV)
#define BNXT_CTX_MEM_SEG_TIM (BNXT_CTX_MEM_SEG_ID_START + BNXT_CTX_TIM)
#define BNXT_CTX_MEM_SEG_SRT 0x1
#define BNXT_CTX_MEM_SEG_SRT2 0x2
#define BNXT_CTX_MEM_SEG_CRT 0x3
#define BNXT_CTX_MEM_SEG_CRT2 0x4
#define BNXT_CTX_MEM_SEG_RIGP0 0x5
#define BNXT_CTX_MEM_SEG_L2HWRM 0x6
#define BNXT_CTX_MEM_SEG_REHWRM 0x7
#define BNXT_CTX_MEM_SEG_CA0 0x8
#define BNXT_CTX_MEM_SEG_CA1 0x9
#define BNXT_CTX_MEM_SEG_CA2 0xa
#define BNXT_CTX_MEM_SEG_RIGP1 0xb
#define BNXT_CTX_MEM_SEG_QPC 0xc
#define BNXT_CTX_MEM_SEG_KONG 0xd
#define BNXT_CRASH_DUMP_LEN (8 << 20)
#define COREDUMP_LIST_BUF_LEN 2048
#define COREDUMP_RETRIEVE_BUF_LEN 4096
#define BNXT_SEG_HDR_LEN sizeof(struct bnxt_coredump_segment_hdr)
#define BNXT_SEG_RCD_LEN sizeof(struct bnxt_driver_segment_record)
struct bnxt_coredump {
void *data;
int data_size;
u16 total_segs;
};
#define BNXT_COREDUMP_BUF_LEN(len) ((len) - sizeof(struct bnxt_coredump_record))
struct bnxt_hwrm_dbg_dma_info {
void *dest_buf;
int dest_buf_size;
u16 dma_len;
u16 seq_off;
u16 data_len_off;
u16 segs;
u32 seg_start;
u32 buf_len;
};
struct hwrm_dbg_cmn_input {
__le16 req_type;
__le16 cmpl_ring;
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
__le64 host_dest_addr;
__le32 host_buf_len;
};
struct hwrm_dbg_cmn_output {
__le16 error_code;
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
u8 flags;
#define HWRM_DBG_CMN_FLAGS_MORE 1
};
#define BNXT_DBG_FL_CR_DUMP_SIZE_SOC \
DBG_QCFG_REQ_FLAGS_CRASHDUMP_SIZE_FOR_DEST_DEST_SOC_DDR
#define BNXT_DBG_FL_CR_DUMP_SIZE_HOST \
DBG_QCFG_REQ_FLAGS_CRASHDUMP_SIZE_FOR_DEST_DEST_HOST_DDR
#define BNXT_DBG_CR_DUMP_MDM_CFG_DDR \
DBG_CRASHDUMP_MEDIUM_CFG_REQ_TYPE_DDR
void bnxt_fill_coredump_seg_hdr(struct bnxt *bp,
struct bnxt_coredump_segment_hdr *seg_hdr,
struct coredump_segment_record *seg_rec,
u32 seg_len, int status, u32 duration,
u32 instance, u32 comp_id, u32 seg_id);
int bnxt_get_coredump(struct bnxt *bp, u16 dump_type, void *buf, u32 *dump_len);
int bnxt_hwrm_get_dump_len(struct bnxt *bp, u16 dump_type, u32 *dump_len);
u32 bnxt_get_coredump_length(struct bnxt *bp, u16 dump_type);
#endif
@@ -0,0 +1,801 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
* Copyright (c) 2016-2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/rtnetlink.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <rdma/ib_verbs.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_dcb.h"
#ifdef CONFIG_BNXT_DCB
static int bnxt_queue_to_tc(struct bnxt *bp, u8 queue_id)
{
int i, j;
for (i = 0; i < bp->max_tc; i++) {
if (bp->q_info[i].queue_id == queue_id) {
for (j = 0; j < bp->max_tc; j++) {
if (bp->tc_to_qidx[j] == i)
return j;
}
}
}
return -EINVAL;
}
static int bnxt_hwrm_queue_pri2cos_cfg(struct bnxt *bp, struct ieee_ets *ets)
{
struct hwrm_queue_pri2cos_cfg_input *req;
u8 *pri2cos;
int rc, i;
rc = hwrm_req_init(bp, req, HWRM_QUEUE_PRI2COS_CFG);
if (rc)
return rc;
req->flags = cpu_to_le32(QUEUE_PRI2COS_CFG_REQ_FLAGS_PATH_BIDIR |
QUEUE_PRI2COS_CFG_REQ_FLAGS_IVLAN);
pri2cos = &req->pri0_cos_queue_id;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
u8 qidx;
req->enables |= cpu_to_le32(
QUEUE_PRI2COS_CFG_REQ_ENABLES_PRI0_COS_QUEUE_ID << i);
qidx = bp->tc_to_qidx[ets->prio_tc[i]];
pri2cos[i] = bp->q_info[qidx].queue_id;
}
return hwrm_req_send(bp, req);
}
static int bnxt_hwrm_queue_pri2cos_qcfg(struct bnxt *bp, struct ieee_ets *ets)
{
struct hwrm_queue_pri2cos_qcfg_output *resp;
struct hwrm_queue_pri2cos_qcfg_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_QUEUE_PRI2COS_QCFG);
if (rc)
return rc;
req->flags = cpu_to_le32(QUEUE_PRI2COS_QCFG_REQ_FLAGS_IVLAN);
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
if (!rc) {
u8 *pri2cos = &resp->pri0_cos_queue_id;
int i;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
u8 queue_id = pri2cos[i];
int tc;
tc = bnxt_queue_to_tc(bp, queue_id);
if (tc >= 0)
ets->prio_tc[i] = tc;
}
}
hwrm_req_drop(bp, req);
return rc;
}
static int bnxt_hwrm_queue_cos2bw_cfg(struct bnxt *bp, struct ieee_ets *ets,
u8 max_tc)
{
struct hwrm_queue_cos2bw_cfg_input *req;
struct bnxt_cos2bw_cfg cos2bw;
int rc, i;
rc = hwrm_req_init(bp, req, HWRM_QUEUE_COS2BW_CFG);
if (rc)
return rc;
for (i = 0; i < max_tc; i++) {
u8 qidx = bp->tc_to_qidx[i];
req->enables |= cpu_to_le32(
QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID0_VALID <<
qidx);
memset(&cos2bw, 0, sizeof(cos2bw));
cos2bw.queue_id = bp->q_info[qidx].queue_id;
if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_STRICT) {
cos2bw.tsa =
QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_SP;
cos2bw.pri_lvl = i;
} else {
cos2bw.tsa =
QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_ETS;
cos2bw.bw_weight = ets->tc_tx_bw[i];
/* older firmware requires min_bw to be set to the
* same weight value in percent.
*/
cos2bw.min_bw =
cpu_to_le32((ets->tc_tx_bw[i] * 100) |
BW_VALUE_UNIT_PERCENT1_100);
}
if (qidx == 0) {
req->queue_id0 = cos2bw.queue_id;
req->queue_id0_min_bw = cos2bw.min_bw;
req->queue_id0_max_bw = cos2bw.max_bw;
req->queue_id0_tsa_assign = cos2bw.tsa;
req->queue_id0_pri_lvl = cos2bw.pri_lvl;
req->queue_id0_bw_weight = cos2bw.bw_weight;
} else {
memcpy(&req->cfg[i - 1], &cos2bw.cfg, sizeof(cos2bw.cfg));
}
}
return hwrm_req_send(bp, req);
}
static int bnxt_hwrm_queue_cos2bw_qcfg(struct bnxt *bp, struct ieee_ets *ets)
{
struct hwrm_queue_cos2bw_qcfg_output *resp;
struct hwrm_queue_cos2bw_qcfg_input *req;
struct bnxt_cos2bw_cfg cos2bw;
int rc, i;
rc = hwrm_req_init(bp, req, HWRM_QUEUE_COS2BW_QCFG);
if (rc)
return rc;
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
if (rc) {
hwrm_req_drop(bp, req);
return rc;
}
for (i = 0; i < bp->max_tc; i++) {
int tc;
if (i == 0) {
cos2bw.queue_id = resp->queue_id0;
cos2bw.min_bw = resp->queue_id0_min_bw;
cos2bw.max_bw = resp->queue_id0_max_bw;
cos2bw.tsa = resp->queue_id0_tsa_assign;
cos2bw.pri_lvl = resp->queue_id0_pri_lvl;
cos2bw.bw_weight = resp->queue_id0_bw_weight;
} else {
memcpy(&cos2bw.cfg, &resp->cfg[i - 1], sizeof(cos2bw.cfg));
}
tc = bnxt_queue_to_tc(bp, cos2bw.queue_id);
if (tc < 0)
continue;
if (cos2bw.tsa ==
QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_SP) {
ets->tc_tsa[tc] = IEEE_8021QAZ_TSA_STRICT;
} else {
ets->tc_tsa[tc] = IEEE_8021QAZ_TSA_ETS;
ets->tc_tx_bw[tc] = cos2bw.bw_weight;
}
}
hwrm_req_drop(bp, req);
return 0;
}
static int bnxt_queue_remap(struct bnxt *bp, unsigned int lltc_mask)
{
unsigned long qmap = 0;
int max = bp->max_tc;
int i, j, rc;
/* Assign lossless TCs first */
for (i = 0, j = 0; i < max; ) {
if (lltc_mask & (1 << i)) {
if (BNXT_LLQ(bp->q_info[j].queue_profile)) {
bp->tc_to_qidx[i] = j;
__set_bit(j, &qmap);
i++;
}
j++;
continue;
}
i++;
}
for (i = 0, j = 0; i < max; i++) {
if (lltc_mask & (1 << i))
continue;
j = find_next_zero_bit(&qmap, max, j);
bp->tc_to_qidx[i] = j;
__set_bit(j, &qmap);
j++;
}
if (netif_running(bp->dev)) {
bnxt_close_nic(bp, false, false);
rc = bnxt_open_nic(bp, false, false);
if (rc) {
netdev_warn(bp->dev, "failed to open NIC, rc = %d\n", rc);
return rc;
}
}
if (bp->ieee_ets) {
int tc = bp->num_tc;
if (!tc)
tc = 1;
rc = bnxt_hwrm_queue_cos2bw_cfg(bp, bp->ieee_ets, tc);
if (rc) {
netdev_warn(bp->dev, "failed to config BW, rc = %d\n", rc);
return rc;
}
rc = bnxt_hwrm_queue_pri2cos_cfg(bp, bp->ieee_ets);
if (rc) {
netdev_warn(bp->dev, "failed to config prio, rc = %d\n", rc);
return rc;
}
}
return 0;
}
static int bnxt_hwrm_queue_pfc_cfg(struct bnxt *bp, struct ieee_pfc *pfc)
{
struct hwrm_queue_pfcenable_cfg_input *req;
struct ieee_ets *my_ets = bp->ieee_ets;
unsigned int tc_mask = 0, pri_mask = 0;
u8 i, pri, lltc_count = 0;
bool need_q_remap = false;
int rc;
if (!my_ets)
return -EINVAL;
for (i = 0; i < bp->max_tc; i++) {
for (pri = 0; pri < IEEE_8021QAZ_MAX_TCS; pri++) {
if ((pfc->pfc_en & (1 << pri)) &&
(my_ets->prio_tc[pri] == i)) {
pri_mask |= 1 << pri;
tc_mask |= 1 << i;
}
}
if (tc_mask & (1 << i))
lltc_count++;
}
if (lltc_count > bp->max_lltc)
return -EINVAL;
for (i = 0; i < bp->max_tc; i++) {
if (tc_mask & (1 << i)) {
u8 qidx = bp->tc_to_qidx[i];
if (!BNXT_LLQ(bp->q_info[qidx].queue_profile)) {
need_q_remap = true;
break;
}
}
}
if (need_q_remap)
bnxt_queue_remap(bp, tc_mask);
rc = hwrm_req_init(bp, req, HWRM_QUEUE_PFCENABLE_CFG);
if (rc)
return rc;
req->flags = cpu_to_le32(pri_mask);
return hwrm_req_send(bp, req);
}
static int bnxt_hwrm_queue_pfc_qcfg(struct bnxt *bp, struct ieee_pfc *pfc)
{
struct hwrm_queue_pfcenable_qcfg_output *resp;
struct hwrm_queue_pfcenable_qcfg_input *req;
u8 pri_mask;
int rc;
rc = hwrm_req_init(bp, req, HWRM_QUEUE_PFCENABLE_QCFG);
if (rc)
return rc;
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
if (rc) {
hwrm_req_drop(bp, req);
return rc;
}
pri_mask = le32_to_cpu(resp->flags);
pfc->pfc_en = pri_mask;
hwrm_req_drop(bp, req);
return 0;
}
static int bnxt_hwrm_set_dcbx_app(struct bnxt *bp, struct dcb_app *app,
bool add)
{
struct hwrm_fw_set_structured_data_input *set;
struct hwrm_fw_get_structured_data_input *get;
struct hwrm_struct_data_dcbx_app *fw_app;
struct hwrm_struct_hdr *data;
dma_addr_t mapping;
size_t data_len;
int rc, n, i;
if (bp->hwrm_spec_code < 0x10601)
return 0;
rc = hwrm_req_init(bp, get, HWRM_FW_GET_STRUCTURED_DATA);
if (rc)
return rc;
hwrm_req_hold(bp, get);
hwrm_req_alloc_flags(bp, get, GFP_KERNEL | __GFP_ZERO);
n = IEEE_8021QAZ_MAX_TCS;
data_len = sizeof(*data) + sizeof(*fw_app) * n;
data = hwrm_req_dma_slice(bp, get, data_len, &mapping);
if (!data) {
rc = -ENOMEM;
goto set_app_exit;
}
get->dest_data_addr = cpu_to_le64(mapping);
get->structure_id = cpu_to_le16(STRUCT_HDR_STRUCT_ID_DCBX_APP);
get->subtype = cpu_to_le16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
get->count = 0;
rc = hwrm_req_send(bp, get);
if (rc)
goto set_app_exit;
fw_app = (struct hwrm_struct_data_dcbx_app *)(data + 1);
if (data->struct_id != cpu_to_le16(STRUCT_HDR_STRUCT_ID_DCBX_APP)) {
rc = -ENODEV;
goto set_app_exit;
}
n = data->count;
for (i = 0; i < n; i++, fw_app++) {
if (fw_app->protocol_id == cpu_to_be16(app->protocol) &&
fw_app->protocol_selector == app->selector &&
fw_app->priority == app->priority) {
if (add)
goto set_app_exit;
else
break;
}
}
if (add) {
/* append */
n++;
fw_app->protocol_id = cpu_to_be16(app->protocol);
fw_app->protocol_selector = app->selector;
fw_app->priority = app->priority;
fw_app->valid = 1;
} else {
size_t len = 0;
/* not found, nothing to delete */
if (n == i)
goto set_app_exit;
len = (n - 1 - i) * sizeof(*fw_app);
if (len)
memmove(fw_app, fw_app + 1, len);
n--;
memset(fw_app + n, 0, sizeof(*fw_app));
}
data->count = n;
data->len = cpu_to_le16(sizeof(*fw_app) * n);
data->subtype = cpu_to_le16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
rc = hwrm_req_init(bp, set, HWRM_FW_SET_STRUCTURED_DATA);
if (rc)
goto set_app_exit;
set->src_data_addr = cpu_to_le64(mapping);
set->data_len = cpu_to_le16(sizeof(*data) + sizeof(*fw_app) * n);
set->hdr_cnt = 1;
rc = hwrm_req_send(bp, set);
set_app_exit:
hwrm_req_drop(bp, get); /* dropping get request and associated slice */
return rc;
}
static int bnxt_hwrm_queue_dscp_qcaps(struct bnxt *bp)
{
struct hwrm_queue_dscp_qcaps_output *resp;
struct hwrm_queue_dscp_qcaps_input *req;
int rc;
bp->max_dscp_value = 0;
if (bp->hwrm_spec_code < 0x10800 || BNXT_VF(bp))
return 0;
rc = hwrm_req_init(bp, req, HWRM_QUEUE_DSCP_QCAPS);
if (rc)
return rc;
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send_silent(bp, req);
if (!rc) {
bp->max_dscp_value = (1 << resp->num_dscp_bits) - 1;
if (bp->max_dscp_value < 0x3f)
bp->max_dscp_value = 0;
}
hwrm_req_drop(bp, req);
return rc;
}
static int bnxt_hwrm_queue_dscp2pri_cfg(struct bnxt *bp, struct dcb_app *app,
bool add)
{
struct hwrm_queue_dscp2pri_cfg_input *req;
struct bnxt_dscp2pri_entry *dscp2pri;
dma_addr_t mapping;
int rc;
if (bp->hwrm_spec_code < 0x10800)
return 0;
rc = hwrm_req_init(bp, req, HWRM_QUEUE_DSCP2PRI_CFG);
if (rc)
return rc;
dscp2pri = hwrm_req_dma_slice(bp, req, sizeof(*dscp2pri), &mapping);
if (!dscp2pri) {
hwrm_req_drop(bp, req);
return -ENOMEM;
}
req->src_data_addr = cpu_to_le64(mapping);
dscp2pri->dscp = app->protocol;
if (add)
dscp2pri->mask = 0x3f;
else
dscp2pri->mask = 0;
dscp2pri->pri = app->priority;
req->entry_cnt = cpu_to_le16(1);
rc = hwrm_req_send(bp, req);
return rc;
}
static int bnxt_ets_validate(struct bnxt *bp, struct ieee_ets *ets, u8 *tc)
{
int total_ets_bw = 0;
bool zero = false;
u8 max_tc = 0;
int i;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
if (ets->prio_tc[i] > bp->max_tc) {
netdev_err(bp->dev, "priority to TC mapping exceeds TC count %d\n",
ets->prio_tc[i]);
return -EINVAL;
}
if (ets->prio_tc[i] > max_tc)
max_tc = ets->prio_tc[i];
if ((ets->tc_tx_bw[i] || ets->tc_tsa[i]) && i > bp->max_tc)
return -EINVAL;
}
for (i = 0; i < max_tc; i++) {
switch (ets->tc_tsa[i]) {
case IEEE_8021QAZ_TSA_STRICT:
break;
case IEEE_8021QAZ_TSA_ETS:
total_ets_bw += ets->tc_tx_bw[i];
zero = zero || !ets->tc_tx_bw[i];
break;
default:
return -ENOTSUPP;
}
}
if (total_ets_bw > 100) {
netdev_warn(bp->dev, "rejecting ETS config exceeding available bandwidth\n");
return -EINVAL;
}
if (zero && total_ets_bw == 100) {
netdev_warn(bp->dev, "rejecting ETS config starving a TC\n");
return -EINVAL;
}
if (max_tc >= bp->max_tc)
*tc = bp->max_tc;
else
*tc = max_tc + 1;
return 0;
}
static int bnxt_dcbnl_ieee_getets(struct net_device *dev, struct ieee_ets *ets)
{
struct bnxt *bp = netdev_priv(dev);
struct ieee_ets *my_ets = bp->ieee_ets;
int rc;
ets->ets_cap = bp->max_tc;
if (!my_ets) {
if (bp->dcbx_cap & DCB_CAP_DCBX_HOST)
return 0;
my_ets = kzalloc_obj(*my_ets);
if (!my_ets)
return -ENOMEM;
rc = bnxt_hwrm_queue_cos2bw_qcfg(bp, my_ets);
if (rc)
goto error;
rc = bnxt_hwrm_queue_pri2cos_qcfg(bp, my_ets);
if (rc)
goto error;
/* cache result */
bp->ieee_ets = my_ets;
}
ets->cbs = my_ets->cbs;
memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw));
memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw));
memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa));
memcpy(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc));
return 0;
error:
kfree(my_ets);
return rc;
}
static int bnxt_dcbnl_ieee_setets(struct net_device *dev, struct ieee_ets *ets)
{
struct bnxt *bp = netdev_priv(dev);
struct ieee_ets *my_ets = bp->ieee_ets;
u8 max_tc = 0;
int rc, i;
if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) ||
!(bp->dcbx_cap & DCB_CAP_DCBX_HOST))
return -EINVAL;
rc = bnxt_ets_validate(bp, ets, &max_tc);
if (!rc) {
if (!my_ets) {
my_ets = kzalloc_obj(*my_ets);
if (!my_ets)
return -ENOMEM;
/* initialize PRI2TC mappings to invalid value */
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
my_ets->prio_tc[i] = IEEE_8021QAZ_MAX_TCS;
bp->ieee_ets = my_ets;
}
rc = bnxt_setup_mq_tc(dev, max_tc);
if (rc)
return rc;
rc = bnxt_hwrm_queue_cos2bw_cfg(bp, ets, max_tc);
if (rc)
return rc;
rc = bnxt_hwrm_queue_pri2cos_cfg(bp, ets);
if (rc)
return rc;
memcpy(my_ets, ets, sizeof(*my_ets));
}
return rc;
}
static int bnxt_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc)
{
struct bnxt *bp = netdev_priv(dev);
__le64 *stats = bp->port_stats.hw_stats;
struct ieee_pfc *my_pfc = bp->ieee_pfc;
long rx_off, tx_off;
int i, rc;
pfc->pfc_cap = bp->max_lltc;
if (!my_pfc) {
if (bp->dcbx_cap & DCB_CAP_DCBX_HOST)
return 0;
my_pfc = kzalloc_obj(*my_pfc);
if (!my_pfc)
return 0;
bp->ieee_pfc = my_pfc;
rc = bnxt_hwrm_queue_pfc_qcfg(bp, my_pfc);
if (rc)
return 0;
}
pfc->pfc_en = my_pfc->pfc_en;
pfc->mbc = my_pfc->mbc;
pfc->delay = my_pfc->delay;
if (!stats)
return 0;
rx_off = BNXT_RX_STATS_OFFSET(rx_pfc_ena_frames_pri0);
tx_off = BNXT_TX_STATS_OFFSET(tx_pfc_ena_frames_pri0);
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++, rx_off++, tx_off++) {
pfc->requests[i] = le64_to_cpu(*(stats + tx_off));
pfc->indications[i] = le64_to_cpu(*(stats + rx_off));
}
return 0;
}
static int bnxt_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc)
{
struct bnxt *bp = netdev_priv(dev);
struct ieee_pfc *my_pfc = bp->ieee_pfc;
int rc;
if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) ||
!(bp->dcbx_cap & DCB_CAP_DCBX_HOST) ||
(bp->phy_flags & BNXT_PHY_FL_NO_PAUSE))
return -EINVAL;
if (!my_pfc) {
my_pfc = kzalloc_obj(*my_pfc);
if (!my_pfc)
return -ENOMEM;
bp->ieee_pfc = my_pfc;
}
rc = bnxt_hwrm_queue_pfc_cfg(bp, pfc);
if (!rc)
memcpy(my_pfc, pfc, sizeof(*my_pfc));
return rc;
}
static int bnxt_dcbnl_ieee_dscp_app_prep(struct bnxt *bp, struct dcb_app *app)
{
if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) {
if (!bp->max_dscp_value)
return -ENOTSUPP;
if (app->protocol > bp->max_dscp_value)
return -EINVAL;
}
return 0;
}
static int bnxt_dcbnl_ieee_setapp(struct net_device *dev, struct dcb_app *app)
{
struct bnxt *bp = netdev_priv(dev);
int rc;
if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) ||
!(bp->dcbx_cap & DCB_CAP_DCBX_HOST))
return -EINVAL;
rc = bnxt_dcbnl_ieee_dscp_app_prep(bp, app);
if (rc)
return rc;
rc = dcb_ieee_setapp(dev, app);
if (rc)
return rc;
if ((app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
app->protocol == ETH_P_IBOE) ||
(app->selector == IEEE_8021QAZ_APP_SEL_DGRAM &&
app->protocol == ROCE_V2_UDP_DPORT))
rc = bnxt_hwrm_set_dcbx_app(bp, app, true);
if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
rc = bnxt_hwrm_queue_dscp2pri_cfg(bp, app, true);
return rc;
}
static int bnxt_dcbnl_ieee_delapp(struct net_device *dev, struct dcb_app *app)
{
struct bnxt *bp = netdev_priv(dev);
int rc;
if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) ||
!(bp->dcbx_cap & DCB_CAP_DCBX_HOST))
return -EINVAL;
rc = bnxt_dcbnl_ieee_dscp_app_prep(bp, app);
if (rc)
return rc;
rc = dcb_ieee_delapp(dev, app);
if (rc)
return rc;
if ((app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
app->protocol == ETH_P_IBOE) ||
(app->selector == IEEE_8021QAZ_APP_SEL_DGRAM &&
app->protocol == ROCE_V2_UDP_DPORT))
rc = bnxt_hwrm_set_dcbx_app(bp, app, false);
if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
rc = bnxt_hwrm_queue_dscp2pri_cfg(bp, app, false);
return rc;
}
static u8 bnxt_dcbnl_getdcbx(struct net_device *dev)
{
struct bnxt *bp = netdev_priv(dev);
return bp->dcbx_cap;
}
static u8 bnxt_dcbnl_setdcbx(struct net_device *dev, u8 mode)
{
struct bnxt *bp = netdev_priv(dev);
/* All firmware DCBX settings are set in NVRAM */
if (bp->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED)
return 1;
if (mode & DCB_CAP_DCBX_HOST) {
if (BNXT_VF(bp) || (bp->fw_cap & BNXT_FW_CAP_LLDP_AGENT))
return 1;
/* only support IEEE */
if ((mode & DCB_CAP_DCBX_VER_CEE) ||
!(mode & DCB_CAP_DCBX_VER_IEEE))
return 1;
}
if (mode == bp->dcbx_cap)
return 0;
bp->dcbx_cap = mode;
return 0;
}
static const struct dcbnl_rtnl_ops dcbnl_ops = {
.ieee_getets = bnxt_dcbnl_ieee_getets,
.ieee_setets = bnxt_dcbnl_ieee_setets,
.ieee_getpfc = bnxt_dcbnl_ieee_getpfc,
.ieee_setpfc = bnxt_dcbnl_ieee_setpfc,
.ieee_setapp = bnxt_dcbnl_ieee_setapp,
.ieee_delapp = bnxt_dcbnl_ieee_delapp,
.getdcbx = bnxt_dcbnl_getdcbx,
.setdcbx = bnxt_dcbnl_setdcbx,
};
void bnxt_dcb_init(struct bnxt *bp)
{
bp->dcbx_cap = 0;
if (bp->hwrm_spec_code < 0x10501)
return;
bnxt_hwrm_queue_dscp_qcaps(bp);
bp->dcbx_cap = DCB_CAP_DCBX_VER_IEEE;
if (BNXT_PF(bp) && !(bp->fw_cap & BNXT_FW_CAP_LLDP_AGENT))
bp->dcbx_cap |= DCB_CAP_DCBX_HOST;
else if (bp->fw_cap & BNXT_FW_CAP_DCBX_AGENT)
bp->dcbx_cap |= DCB_CAP_DCBX_LLD_MANAGED;
bp->dev->dcbnl_ops = &dcbnl_ops;
}
void bnxt_dcb_free(struct bnxt *bp)
{
kfree(bp->ieee_pfc);
kfree(bp->ieee_ets);
bp->ieee_pfc = NULL;
bp->ieee_ets = NULL;
}
#else
void bnxt_dcb_init(struct bnxt *bp)
{
}
void bnxt_dcb_free(struct bnxt *bp)
{
}
#endif
@@ -0,0 +1,57 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
* Copyright (c) 2016-2018 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_DCB_H
#define BNXT_DCB_H
#include <net/dcbnl.h>
struct bnxt_dcb {
u8 max_tc;
struct ieee_pfc *ieee_pfc;
struct ieee_ets *ieee_ets;
u8 dcbx_cap;
u8 default_pri;
};
struct bnxt_cos2bw_cfg {
u8 pad[3];
struct_group_attr(cfg, __packed,
u8 queue_id;
__le32 min_bw;
__le32 max_bw;
u8 tsa;
u8 pri_lvl;
u8 bw_weight;
);
/* for min_bw / max_bw */
#define BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29)
u8 unused;
};
struct bnxt_dscp2pri_entry {
u8 dscp;
u8 mask;
u8 pri;
};
#define BNXT_LLQ(q_profile) \
((q_profile) == \
QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS_ROCE)
#define BNXT_CNPQ(q_profile) \
((q_profile) == \
QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSY_ROCE_CNP)
#define HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL 0x0300
void bnxt_dcb_init(struct bnxt *bp);
void bnxt_dcb_free(struct bnxt *bp);
#endif
@@ -0,0 +1,107 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2017-2018 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/bnxt/hsi.h>
#include <linux/dim.h>
#include "bnxt.h"
#include "bnxt_debugfs.h"
static struct dentry *bnxt_debug_mnt;
static ssize_t debugfs_dim_read(struct file *filep,
char __user *buffer,
size_t count, loff_t *ppos)
{
struct dim *dim = filep->private_data;
int len;
char *buf;
if (*ppos)
return 0;
if (!dim)
return -ENODEV;
buf = kasprintf(GFP_KERNEL,
"state = %d\n" \
"profile_ix = %d\n" \
"mode = %d\n" \
"tune_state = %d\n" \
"steps_right = %d\n" \
"steps_left = %d\n" \
"tired = %d\n",
dim->state,
dim->profile_ix,
dim->mode,
dim->tune_state,
dim->steps_right,
dim->steps_left,
dim->tired);
if (!buf)
return -ENOMEM;
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static const struct file_operations debugfs_dim_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = debugfs_dim_read,
};
static void debugfs_dim_ring_init(struct dim *dim, int ring_idx,
struct dentry *dd)
{
static char qname[12];
snprintf(qname, sizeof(qname), "%d", ring_idx);
debugfs_create_file(qname, 0600, dd, dim, &debugfs_dim_fops);
}
void bnxt_debug_dev_init(struct bnxt *bp)
{
const char *pname = pci_name(bp->pdev);
struct dentry *dir;
int i;
bp->debugfs_pdev = debugfs_create_dir(pname, bnxt_debug_mnt);
dir = debugfs_create_dir("dim", bp->debugfs_pdev);
/* create files for each rx ring */
for (i = 0; i < bp->cp_nr_rings; i++) {
struct bnxt_cp_ring_info *cpr = &bp->bnapi[i]->cp_ring;
if (cpr && bp->bnapi[i]->rx_ring)
debugfs_dim_ring_init(&cpr->dim, i, dir);
}
}
void bnxt_debug_dev_exit(struct bnxt *bp)
{
if (bp) {
debugfs_remove_recursive(bp->debugfs_pdev);
bp->debugfs_pdev = NULL;
}
}
void bnxt_debug_init(void)
{
bnxt_debug_mnt = debugfs_create_dir("bnxt_en", NULL);
}
void bnxt_debug_exit(void)
{
debugfs_remove_recursive(bnxt_debug_mnt);
}
@@ -0,0 +1,23 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2017-2018 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#ifdef CONFIG_DEBUG_FS
void bnxt_debug_init(void);
void bnxt_debug_exit(void);
void bnxt_debug_dev_init(struct bnxt *bp);
void bnxt_debug_dev_exit(struct bnxt *bp);
#else
static inline void bnxt_debug_init(void) {}
static inline void bnxt_debug_exit(void) {}
static inline void bnxt_debug_dev_init(struct bnxt *bp) {}
static inline void bnxt_debug_dev_exit(struct bnxt *bp) {}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_DEVLINK_H
#define BNXT_DEVLINK_H
/* Struct to hold housekeeping info needed by devlink interface */
struct bnxt_dl {
struct bnxt *bp; /* back ptr to the controlling dev */
bool remote_reset;
};
static inline struct bnxt *bnxt_get_bp_from_dl(struct devlink *dl)
{
return ((struct bnxt_dl *)devlink_priv(dl))->bp;
}
static inline void bnxt_dl_remote_reload(struct bnxt *bp)
{
devlink_remote_reload_actions_performed(bp->dl, 0,
BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
}
static inline bool bnxt_dl_get_remote_reset(struct devlink *dl)
{
return ((struct bnxt_dl *)devlink_priv(dl))->remote_reset;
}
static inline void bnxt_dl_set_remote_reset(struct devlink *dl, bool value)
{
((struct bnxt_dl *)devlink_priv(dl))->remote_reset = value;
}
#define NVM_OFF_MSIX_VEC_PER_PF_MAX 108
#define NVM_OFF_MSIX_VEC_PER_PF_MIN 114
#define NVM_OFF_IGNORE_ARI 164
#define NVM_OFF_RDMA_CAPABLE 161
#define NVM_OFF_DIS_GRE_VER_CHECK 171
#define NVM_OFF_ENABLE_SRIOV 401
#define NVM_OFF_SUPPORT_RDMA 506
#define NVM_OFF_NVM_CFG_VER 602
#define BNXT_NVM_CFG_VER_BITS 8
#define BNXT_NVM_CFG_VER_BYTES 1
#define BNXT_MSIX_VEC_MAX 512
#define BNXT_MSIX_VEC_MIN_MAX 128
enum bnxt_nvm_dir_type {
BNXT_NVM_SHARED_CFG = 40,
BNXT_NVM_PORT_CFG,
BNXT_NVM_FUNC_CFG,
};
struct bnxt_dl_nvm_param {
u16 id;
u16 offset;
u16 dir_type;
u16 nvm_num_bits;
u8 dl_num_bytes;
};
enum bnxt_dl_version_type {
BNXT_VERSION_FIXED,
BNXT_VERSION_RUNNING,
BNXT_VERSION_STORED,
};
void bnxt_devlink_health_fw_report(struct bnxt *bp);
void bnxt_dl_health_fw_status_update(struct bnxt *bp, bool healthy);
void bnxt_dl_health_fw_recovery_done(struct bnxt *bp);
void bnxt_dl_fw_reporters_create(struct bnxt *bp);
void bnxt_dl_fw_reporters_destroy(struct bnxt *bp);
int bnxt_dl_register(struct bnxt *bp);
void bnxt_dl_unregister(struct bnxt *bp);
#endif /* BNXT_DEVLINK_H */
@@ -0,0 +1,31 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2017-2018 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/dim.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
void bnxt_dim_work(struct work_struct *work)
{
struct dim *dim = container_of(work, struct dim, work);
struct bnxt_cp_ring_info *cpr = container_of(dim,
struct bnxt_cp_ring_info,
dim);
struct bnxt_napi *bnapi = container_of(cpr,
struct bnxt_napi,
cp_ring);
struct dim_cq_moder cur_moder =
net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
cpr->rx_ring_coal.coal_ticks = cur_moder.usec;
cpr->rx_ring_coal.coal_bufs = cur_moder.pkts;
bnxt_hwrm_set_ring_coal(bnapi->bp, bnapi);
dim->state = DIM_START_MEASURE;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,77 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
* Copyright (c) 2016-2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_ETHTOOL_H
#define BNXT_ETHTOOL_H
struct bnxt_led_cfg {
u8 led_id;
u8 led_state;
u8 led_color;
u8 unused;
__le16 led_blink_on;
__le16 led_blink_off;
u8 led_group_id;
u8 rsvd;
};
#define BNXT_LED_DFLT_ENA \
(PORT_LED_CFG_REQ_ENABLES_LED0_ID | \
PORT_LED_CFG_REQ_ENABLES_LED0_STATE | \
PORT_LED_CFG_REQ_ENABLES_LED0_BLINK_ON | \
PORT_LED_CFG_REQ_ENABLES_LED0_BLINK_OFF | \
PORT_LED_CFG_REQ_ENABLES_LED0_GROUP_ID)
#define BNXT_LED_DFLT_ENA_SHIFT 6
#define BNXT_LED_DFLT_ENABLES(x) \
cpu_to_le32(BNXT_LED_DFLT_ENA << (BNXT_LED_DFLT_ENA_SHIFT * (x)))
#define BNXT_FW_RESET_AP (ETH_RESET_AP << ETH_RESET_SHARED_SHIFT)
#define BNXT_FW_RESET_CHIP ((ETH_RESET_MGMT | ETH_RESET_IRQ | \
ETH_RESET_DMA | ETH_RESET_FILTER | \
ETH_RESET_OFFLOAD | ETH_RESET_MAC | \
ETH_RESET_PHY | ETH_RESET_RAM) \
<< ETH_RESET_SHARED_SHIFT)
#define BNXT_PXP_REG_LEN 0x3110
#define BNXT_IP_PROTO_FULL_MASK 0xFF
#define BNXT_IP_PROTO_WILDCARD 0x0
extern const struct ethtool_ops bnxt_ethtool_ops;
u32 bnxt_get_rxfh_indir_size(struct net_device *dev);
void _bnxt_fw_to_linkmode(unsigned long *mode, u16 fw_speeds);
u32 bnxt_fw_to_ethtool_speed(u16);
u16 bnxt_get_fw_auto_link_speeds(const unsigned long *mode);
int bnxt_hwrm_nvm_get_dev_info(struct bnxt *bp,
struct hwrm_nvm_get_dev_info_output *nvm_dev_info);
int bnxt_hwrm_firmware_reset(struct net_device *dev, u8 proc_type,
u8 self_reset, u8 flags);
int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware *fw,
u32 install_type, struct netlink_ext_ack *extack);
int bnxt_get_pkginfo(struct net_device *dev, char *ver, int size);
void bnxt_ethtool_init(struct bnxt *bp);
void bnxt_ethtool_free(struct bnxt *bp);
int bnxt_find_nvram_item(struct net_device *dev, u16 type, u16 ordinal,
u16 ext, u16 *index, u32 *item_length,
u32 *data_length);
int bnxt_find_nvram_item(struct net_device *dev, u16 type, u16 ordinal,
u16 ext, u16 *index, u32 *item_length,
u32 *data_length);
int bnxt_flash_nvram(struct net_device *dev, u16 dir_type,
u16 dir_ordinal, u16 dir_ext, u16 dir_attr,
u32 dir_item_len, const u8 *data,
size_t data_len);
int bnxt_get_nvram_item(struct net_device *dev, u32 index, u32 offset,
u32 length, u8 *data);
#endif
@@ -0,0 +1,119 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef __BNXT_FW_HDR_H__
#define __BNXT_FW_HDR_H__
#define BNXT_FIRMWARE_BIN_SIGNATURE 0x1a4d4342 /* "BCM"+0x1a */
#define BNXT_UCODE_TRAILER_SIGNATURE 0x726c7254 /* "Trlr" */
enum SUPPORTED_FAMILY {
DEVICE_5702_3_4_FAMILY, /* 0 - Denali, Vinson, K2 */
DEVICE_5705_FAMILY, /* 1 - Bachelor */
DEVICE_SHASTA_FAMILY, /* 2 - 5751 */
DEVICE_5706_FAMILY, /* 3 - Teton */
DEVICE_5714_FAMILY, /* 4 - Hamilton */
DEVICE_STANFORD_FAMILY, /* 5 - 5755 */
DEVICE_STANFORD_ME_FAMILY, /* 6 - 5756 */
DEVICE_SOLEDAD_FAMILY, /* 7 - 5761[E] */
DEVICE_CILAI_FAMILY, /* 8 - 57780/60/90/91 */
DEVICE_ASPEN_FAMILY, /* 9 - 57781/85/61/65/91/95 */
DEVICE_ASPEN_PLUS_FAMILY, /* 10 - 57786 */
DEVICE_LOGAN_FAMILY, /* 11 - Any device in the Logan family
*/
DEVICE_LOGAN_5762, /* 12 - Logan Enterprise (aka Columbia)
*/
DEVICE_LOGAN_57767, /* 13 - Logan Client */
DEVICE_LOGAN_57787, /* 14 - Logan Consumer */
DEVICE_LOGAN_5725, /* 15 - Logan Server (TruManage-enabled)
*/
DEVICE_SAWTOOTH_FAMILY, /* 16 - 5717/18 */
DEVICE_COTOPAXI_FAMILY, /* 17 - 5719 */
DEVICE_SNAGGLETOOTH_FAMILY, /* 18 - 5720 */
DEVICE_CUMULUS_FAMILY, /* 19 - Cumulus/Whitney */
MAX_DEVICE_FAMILY
};
enum SUPPORTED_CODE {
CODE_ASF1, /* 0 - ASF VERSION 1.03 <deprecated> */
CODE_ASF2, /* 1 - ASF VERSION 2.00 <deprecated> */
CODE_PASSTHRU, /* 2 - PassThru <deprecated> */
CODE_PT_SEC, /* 3 - PassThru with security <deprecated> */
CODE_UMP, /* 4 - UMP <deprecated> */
CODE_BOOT, /* 5 - Bootcode */
CODE_DASH, /* 6 - TruManage (DASH + ASF + PMCI)
* Management firmwares
*/
CODE_MCTP_PASSTHRU, /* 7 - NCSI / MCTP Passt-hrough firmware */
CODE_PM_OFFLOAD, /* 8 - Power-Management Proxy Offload firmwares
*/
CODE_MDNS_SD_OFFLOAD, /* 9 - Multicast DNS Service Discovery Proxys
* Offload firmware
*/
CODE_DISC_OFFLOAD, /* 10 - Discovery Offload firmware */
CODE_MUSTANG, /* 11 - I2C Error reporting APE firmwares
* <deprecated>
*/
CODE_ARP_BATCH, /* 12 - ARP Batch firmware */
CODE_SMASH, /* 13 - TruManage (SMASH + DCMI/IPMI + PMCI)
* Management firmware
*/
CODE_APE_DIAG, /* 14 - APE Test Diag firmware */
CODE_APE_PATCH, /* 15 - APE Patch firmware */
CODE_TANG_PATCH, /* 16 - TANG Patch firmware */
CODE_KONG_FW, /* 17 - KONG firmware */
CODE_KONG_PATCH, /* 18 - KONG Patch firmware */
CODE_BONO_FW, /* 19 - BONO firmware */
CODE_BONO_PATCH, /* 20 - BONO Patch firmware */
CODE_CHIMP_PATCH, /* 21 - ChiMP Patch firmware */
MAX_CODE_TYPE,
};
enum SUPPORTED_MEDIA {
MEDIA_COPPER, /* 0 */
MEDIA_FIBER, /* 1 */
MEDIA_NONE, /* 2 */
MEDIA_COPPER_FIBER, /* 3 */
MAX_MEDIA_TYPE,
};
struct bnxt_fw_header {
__le32 signature; /* constains the constant value of
* BNXT_FIRMWARE_BIN_SIGNATURE
*/
u8 flags; /* reserved for ChiMP use */
u8 code_type; /* enum SUPPORTED_CODE */
u8 device; /* enum SUPPORTED_FAMILY */
u8 media; /* enum SUPPORTED_MEDIA */
u8 version[16]; /* the null terminated version string to
* indicate the version of the
* file, this will be copied from the binary
* file version string
*/
u8 build;
u8 revision;
u8 minor_ver;
u8 major_ver;
};
/* Microcode and pre-boot software/firmware trailer: */
struct bnxt_ucode_trailer {
u8 rsa_sig[256];
__le16 flags;
u8 version_format;
u8 version_length;
u8 version[16];
__le16 dir_type;
__le16 trailer_length;
__le32 sig; /* BNXT_UCODE_TRAILER_SIGNATURE */
__le32 chksum; /* CRC-32 */
};
#endif
@@ -0,0 +1,240 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Broadcom NetXtreme-C/E network driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/netdev_queues.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/udp.h>
#include <net/tso.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_gso.h"
static u32 bnxt_sw_gso_lhint(unsigned int len)
{
if (len <= 512)
return TX_BD_FLAGS_LHINT_512_AND_SMALLER;
else if (len <= 1023)
return TX_BD_FLAGS_LHINT_512_TO_1023;
else if (len <= 2047)
return TX_BD_FLAGS_LHINT_1024_TO_2047;
else
return TX_BD_FLAGS_LHINT_2048_AND_LARGER;
}
netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
struct netdev_queue *txq,
struct sk_buff *skb)
{
unsigned int last_unmap_len __maybe_unused = 0;
dma_addr_t last_unmap_addr __maybe_unused = 0;
struct bnxt_sw_tx_bd *last_unmap_buf = NULL;
unsigned int hdr_len, mss, num_segs;
struct pci_dev *pdev = bp->pdev;
unsigned int total_payload;
struct tso_dma_map map;
u32 vlan_tag_flags = 0;
int i, bds_needed;
struct tso_t tso;
u16 cfa_action;
__le32 csum;
u16 prod;
hdr_len = tso_start(skb, &tso);
mss = skb_shinfo(skb)->gso_size;
total_payload = skb->len - hdr_len;
num_segs = DIV_ROUND_UP(total_payload, mss);
if (unlikely(num_segs <= 1))
goto drop;
/* Upper bound on the number of descriptors needed.
*
* Each segment uses 1 long BD + 1 ext BD + payload BDs, which is
* at most num_segs + nr_frags (each frag boundary crossing adds at
* most 1 extra BD).
*/
bds_needed = 3 * num_segs + skb_shinfo(skb)->nr_frags + 1;
if (unlikely(bnxt_tx_avail(bp, txr) < bds_needed)) {
netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
bp->tx_wake_thresh);
return NETDEV_TX_BUSY;
}
/* BD backpressure alone cannot prevent overwriting in-flight
* headers in the inline buffer. Check slot availability directly.
*/
if (!netif_txq_maybe_stop(txq, bnxt_inline_avail(txr),
num_segs, num_segs))
return NETDEV_TX_BUSY;
if (unlikely(tso_dma_map_init(&map, &pdev->dev, skb, hdr_len)))
goto drop;
cfa_action = bnxt_xmit_get_cfa_action(skb);
if (skb_vlan_tag_present(skb)) {
vlan_tag_flags = TX_BD_CFA_META_KEY_VLAN |
skb_vlan_tag_get(skb);
if (skb->vlan_proto == htons(ETH_P_8021Q))
vlan_tag_flags |= 1 << TX_BD_CFA_META_TPID_SHIFT;
}
csum = cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
if (!tso.ipv6)
csum |= cpu_to_le32(TX_BD_FLAGS_IP_CKSUM);
prod = txr->tx_prod;
for (i = 0; i < num_segs; i++) {
unsigned int seg_payload = min_t(unsigned int, mss,
total_payload - i * mss);
u16 slot = (txr->tx_inline_prod + i) &
(BNXT_SW_USO_MAX_SEGS - 1);
struct bnxt_sw_tx_bd *tx_buf;
unsigned int mapping_len;
dma_addr_t this_hdr_dma;
unsigned int chunk_len;
unsigned int offset;
dma_addr_t dma_addr;
struct tx_bd *txbd;
struct udphdr *uh;
void *this_hdr;
int bd_count;
bool last;
u32 flags;
last = (i == num_segs - 1);
offset = slot * TSO_HEADER_SIZE;
this_hdr = txr->tx_inline_buf + offset;
this_hdr_dma = txr->tx_inline_dma + offset;
tso_build_hdr(skb, this_hdr, &tso, seg_payload, last);
/* Zero stale csum fields copied from the original skb;
* HW offload recomputes from scratch.
*/
uh = this_hdr + skb_transport_offset(skb);
uh->check = 0;
if (!tso.ipv6) {
struct iphdr *iph = this_hdr + skb_network_offset(skb);
iph->check = 0;
}
dma_sync_single_for_device(&pdev->dev, this_hdr_dma,
hdr_len, DMA_TO_DEVICE);
bd_count = tso_dma_map_count(&map, seg_payload);
tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
tx_buf->skb = skb;
tx_buf->nr_frags = bd_count;
tx_buf->is_push = 0;
tx_buf->is_ts_pkt = 0;
dma_unmap_addr_set(tx_buf, mapping, this_hdr_dma);
dma_unmap_len_set(tx_buf, len, 0);
if (last) {
tx_buf->is_sw_gso = BNXT_SW_GSO_LAST;
tso_dma_map_completion_save(&map, &tx_buf->sw_gso_cstate);
} else {
tx_buf->is_sw_gso = BNXT_SW_GSO_MID;
}
flags = (hdr_len << TX_BD_LEN_SHIFT) |
TX_BD_TYPE_LONG_TX_BD |
TX_BD_CNT(2 + bd_count);
flags |= bnxt_sw_gso_lhint(hdr_len + seg_payload);
txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
txbd->tx_bd_haddr = cpu_to_le64(this_hdr_dma);
txbd->tx_bd_opaque = SET_TX_OPAQUE(bp, txr, prod,
2 + bd_count);
prod = NEXT_TX(prod);
bnxt_init_ext_bd(bp, txr, prod, csum,
vlan_tag_flags, cfa_action);
/* set dma_unmap_len on the LAST BD touching each
* region. Since completions are in-order, the last segment
* completes after all earlier ones, so the unmap is safe.
*/
while (tso_dma_map_next(&map, &dma_addr, &chunk_len,
&mapping_len, seg_payload)) {
prod = NEXT_TX(prod);
txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
txbd->tx_bd_haddr = cpu_to_le64(dma_addr);
dma_unmap_addr_set(tx_buf, mapping, dma_addr);
dma_unmap_len_set(tx_buf, len, 0);
tx_buf->skb = NULL;
tx_buf->is_sw_gso = 0;
if (mapping_len) {
if (last_unmap_buf) {
dma_unmap_addr_set(last_unmap_buf,
mapping,
last_unmap_addr);
dma_unmap_len_set(last_unmap_buf,
len,
last_unmap_len);
}
last_unmap_addr = dma_addr;
last_unmap_len = mapping_len;
}
last_unmap_buf = tx_buf;
flags = chunk_len << TX_BD_LEN_SHIFT;
txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
txbd->tx_bd_opaque = 0;
seg_payload -= chunk_len;
}
txbd->tx_bd_len_flags_type |=
cpu_to_le32(TX_BD_FLAGS_PACKET_END);
prod = NEXT_TX(prod);
}
if (last_unmap_buf) {
dma_unmap_addr_set(last_unmap_buf, mapping, last_unmap_addr);
dma_unmap_len_set(last_unmap_buf, len, last_unmap_len);
}
txr->tx_inline_prod += num_segs;
netdev_tx_sent_queue(txq, skb->len);
WRITE_ONCE(txr->tx_prod, prod);
/* Sync BDs before doorbell */
wmb();
bnxt_db_write(bp, &txr->tx_db, prod);
if (unlikely(bnxt_tx_avail(bp, txr) <= bp->tx_wake_thresh))
netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
bp->tx_wake_thresh);
return NETDEV_TX_OK;
drop:
dev_kfree_skb_any(skb);
dev_core_stats_tx_dropped_inc(bp->dev);
return NETDEV_TX_OK;
}
@@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Broadcom NetXtreme-C/E network driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_GSO_H
#define BNXT_GSO_H
/* Maximum segments the stack may send in a single SW USO skb.
* This caps gso_max_segs for NICs without HW USO support.
*/
#define BNXT_SW_USO_MAX_SEGS 64
/* Worst-case TX descriptors consumed by one SW USO packet:
* Each segment: 1 long BD + 1 ext BD + payload BDs.
* Total payload BDs across all segs <= num_segs + nr_frags (each frag
* boundary crossing adds at most 1 extra BD).
* So: 3 * max_segs + MAX_SKB_FRAGS + 1 = 3 * 64 + 17 + 1 = 210.
*/
#define BNXT_SW_USO_MAX_DESCS (3 * BNXT_SW_USO_MAX_SEGS + MAX_SKB_FRAGS + 1)
static inline u16 bnxt_inline_avail(struct bnxt_tx_ring_info *txr)
{
return BNXT_SW_USO_MAX_SEGS -
(u16)(txr->tx_inline_prod - READ_ONCE(txr->tx_inline_cons));
}
static inline int bnxt_min_tx_desc_cnt(struct bnxt *bp,
netdev_features_t features)
{
if (!(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
(features & NETIF_F_GSO_UDP_L4))
return BNXT_SW_USO_MAX_DESCS;
return BNXT_MIN_TX_DESC_CNT;
}
netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
struct netdev_queue *txq,
struct sk_buff *skb);
#endif
@@ -0,0 +1,241 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2023 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/pci.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_hwmon.h"
void bnxt_hwmon_notify_event(struct bnxt *bp)
{
u32 attr;
if (!bp->hwmon_dev)
return;
switch (bp->thermal_threshold_type) {
case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_WARN:
attr = hwmon_temp_max_alarm;
break;
case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_CRITICAL:
attr = hwmon_temp_crit_alarm;
break;
case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_FATAL:
case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_SHUTDOWN:
attr = hwmon_temp_emergency_alarm;
break;
default:
return;
}
hwmon_notify_event(&bp->pdev->dev, hwmon_temp, attr, 0);
}
static int bnxt_hwrm_temp_query(struct bnxt *bp, u8 *temp)
{
struct hwrm_temp_monitor_query_output *resp;
struct hwrm_temp_monitor_query_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_TEMP_MONITOR_QUERY);
if (rc)
return rc;
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send_silent(bp, req);
if (rc)
goto drop_req;
if (temp) {
*temp = resp->temp;
} else if (resp->flags &
TEMP_MONITOR_QUERY_RESP_FLAGS_THRESHOLD_VALUES_AVAILABLE) {
bp->fw_cap |= BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED;
bp->warn_thresh_temp = resp->warn_threshold;
bp->crit_thresh_temp = resp->critical_threshold;
bp->fatal_thresh_temp = resp->fatal_threshold;
bp->shutdown_thresh_temp = resp->shutdown_threshold;
}
drop_req:
hwrm_req_drop(bp, req);
return rc;
}
static umode_t bnxt_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct bnxt *bp = _data;
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input:
return 0444;
case hwmon_temp_max:
case hwmon_temp_crit:
case hwmon_temp_emergency:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
case hwmon_temp_emergency_alarm:
if (!(bp->fw_cap & BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED))
return 0;
return 0444;
default:
return 0;
}
}
static int bnxt_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
struct bnxt *bp = dev_get_drvdata(dev);
u8 temp = 0;
int rc;
switch (attr) {
case hwmon_temp_input:
rc = bnxt_hwrm_temp_query(bp, &temp);
if (!rc)
*val = temp * 1000;
return rc;
case hwmon_temp_max:
*val = bp->warn_thresh_temp * 1000;
return 0;
case hwmon_temp_crit:
*val = bp->crit_thresh_temp * 1000;
return 0;
case hwmon_temp_emergency:
*val = bp->fatal_thresh_temp * 1000;
return 0;
case hwmon_temp_max_alarm:
rc = bnxt_hwrm_temp_query(bp, &temp);
if (!rc)
*val = temp >= bp->warn_thresh_temp;
return rc;
case hwmon_temp_crit_alarm:
rc = bnxt_hwrm_temp_query(bp, &temp);
if (!rc)
*val = temp >= bp->crit_thresh_temp;
return rc;
case hwmon_temp_emergency_alarm:
rc = bnxt_hwrm_temp_query(bp, &temp);
if (!rc)
*val = temp >= bp->fatal_thresh_temp;
return rc;
default:
return -EOPNOTSUPP;
}
}
static const struct hwmon_channel_info *bnxt_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
HWMON_T_EMERGENCY | HWMON_T_MAX_ALARM |
HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM),
NULL
};
static const struct hwmon_ops bnxt_hwmon_ops = {
.is_visible = bnxt_hwmon_is_visible,
.read = bnxt_hwmon_read,
};
static const struct hwmon_chip_info bnxt_hwmon_chip_info = {
.ops = &bnxt_hwmon_ops,
.info = bnxt_hwmon_info,
};
static ssize_t temp1_shutdown_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct bnxt *bp = dev_get_drvdata(dev);
return sysfs_emit(buf, "%u\n", bp->shutdown_thresh_temp * 1000);
}
static ssize_t temp1_shutdown_alarm_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct bnxt *bp = dev_get_drvdata(dev);
u8 temp;
int rc;
rc = bnxt_hwrm_temp_query(bp, &temp);
if (rc)
return -EIO;
return sysfs_emit(buf, "%u\n", temp >= bp->shutdown_thresh_temp);
}
static DEVICE_ATTR_RO(temp1_shutdown);
static DEVICE_ATTR_RO(temp1_shutdown_alarm);
static struct attribute *bnxt_temp_extra_attrs[] = {
&dev_attr_temp1_shutdown.attr,
&dev_attr_temp1_shutdown_alarm.attr,
NULL,
};
static umode_t bnxt_temp_extra_attrs_visible(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = kobj_to_dev(kobj);
struct bnxt *bp = dev_get_drvdata(dev);
/* Shutdown temperature setting in NVM is optional */
if (!(bp->fw_cap & BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED) ||
!bp->shutdown_thresh_temp)
return 0;
return attr->mode;
}
static const struct attribute_group bnxt_temp_extra_group = {
.attrs = bnxt_temp_extra_attrs,
.is_visible = bnxt_temp_extra_attrs_visible,
};
__ATTRIBUTE_GROUPS(bnxt_temp_extra);
void bnxt_hwmon_uninit(struct bnxt *bp)
{
if (bp->hwmon_dev) {
hwmon_device_unregister(bp->hwmon_dev);
bp->hwmon_dev = NULL;
}
}
void bnxt_hwmon_init(struct bnxt *bp)
{
struct pci_dev *pdev = bp->pdev;
int rc;
/* temp1_xxx is only sensor, ensure not registered if it will fail */
rc = bnxt_hwrm_temp_query(bp, NULL);
if (rc == -EACCES || rc == -EOPNOTSUPP) {
bnxt_hwmon_uninit(bp);
return;
}
if (bp->hwmon_dev)
return;
bp->hwmon_dev = hwmon_device_register_with_info(&pdev->dev,
DRV_MODULE_NAME, bp,
&bnxt_hwmon_chip_info,
bnxt_temp_extra_groups);
if (IS_ERR(bp->hwmon_dev)) {
bp->hwmon_dev = NULL;
dev_warn(&pdev->dev, "Cannot register hwmon device\n");
}
}
@@ -0,0 +1,30 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2023 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_HWMON_H
#define BNXT_HWMON_H
#ifdef CONFIG_BNXT_HWMON
void bnxt_hwmon_notify_event(struct bnxt *bp);
void bnxt_hwmon_uninit(struct bnxt *bp);
void bnxt_hwmon_init(struct bnxt *bp);
#else
static inline void bnxt_hwmon_notify_event(struct bnxt *bp)
{
}
static inline void bnxt_hwmon_uninit(struct bnxt *bp)
{
}
static inline void bnxt_hwmon_init(struct bnxt *bp)
{
}
#endif
#endif
@@ -0,0 +1,819 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2020 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <asm/byteorder.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/errno.h>
#include <linux/ethtool.h>
#include <linux/if_ether.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
static u64 hwrm_calc_sentinel(struct bnxt_hwrm_ctx *ctx, u16 req_type)
{
return (((uintptr_t)ctx) + req_type) ^ BNXT_HWRM_SENTINEL;
}
/**
* __hwrm_req_init() - Initialize an HWRM request.
* @bp: The driver context.
* @req: A pointer to the request pointer to initialize.
* @req_type: The request type. This will be converted to the little endian
* before being written to the req_type field of the returned request.
* @req_len: The length of the request to be allocated.
*
* Allocate DMA resources and initialize a new HWRM request object of the
* given type. The response address field in the request is configured with
* the DMA bus address that has been mapped for the response and the passed
* request is pointed to kernel virtual memory mapped for the request (such
* that short_input indirection can be accomplished without copying). The
* request’s target and completion ring are initialized to default values and
* can be overridden by writing to the returned request object directly.
*
* The initialized request can be further customized by writing to its fields
* directly, taking care to covert such fields to little endian. The request
* object will be consumed (and all its associated resources release) upon
* passing it to hwrm_req_send() unless ownership of the request has been
* claimed by the caller via a call to hwrm_req_hold(). If the request is not
* consumed, either because it is never sent or because ownership has been
* claimed, then it must be released by a call to hwrm_req_drop().
*
* Return: zero on success, negative error code otherwise:
* E2BIG: the type of request pointer is too large to fit.
* ENOMEM: an allocation failure occurred.
*/
int __hwrm_req_init(struct bnxt *bp, void **req, u16 req_type, u32 req_len)
{
struct bnxt_hwrm_ctx *ctx;
dma_addr_t dma_handle;
u8 *req_addr;
if (req_len > BNXT_HWRM_CTX_OFFSET)
return -E2BIG;
req_addr = dma_pool_alloc(bp->hwrm_dma_pool, GFP_KERNEL | __GFP_ZERO,
&dma_handle);
if (!req_addr)
return -ENOMEM;
ctx = (struct bnxt_hwrm_ctx *)(req_addr + BNXT_HWRM_CTX_OFFSET);
/* safety first, sentinel used to check for invalid requests */
ctx->sentinel = hwrm_calc_sentinel(ctx, req_type);
ctx->req_len = req_len;
ctx->req = (struct input *)req_addr;
ctx->resp = (struct output *)(req_addr + BNXT_HWRM_RESP_OFFSET);
ctx->dma_handle = dma_handle;
ctx->flags = 0; /* __GFP_ZERO, but be explicit regarding ownership */
ctx->timeout = bp->hwrm_cmd_timeout ?: DFLT_HWRM_CMD_TIMEOUT;
ctx->allocated = BNXT_HWRM_DMA_SIZE - BNXT_HWRM_CTX_OFFSET;
ctx->gfp = GFP_KERNEL;
ctx->slice_addr = NULL;
/* initialize common request fields */
ctx->req->req_type = cpu_to_le16(req_type);
ctx->req->resp_addr = cpu_to_le64(dma_handle + BNXT_HWRM_RESP_OFFSET);
ctx->req->cmpl_ring = cpu_to_le16(BNXT_HWRM_NO_CMPL_RING);
ctx->req->target_id = cpu_to_le16(BNXT_HWRM_TARGET);
*req = ctx->req;
return 0;
}
static struct bnxt_hwrm_ctx *__hwrm_ctx(struct bnxt *bp, u8 *req_addr)
{
void *ctx_addr = req_addr + BNXT_HWRM_CTX_OFFSET;
struct input *req = (struct input *)req_addr;
struct bnxt_hwrm_ctx *ctx = ctx_addr;
u64 sentinel;
if (!req) {
/* can only be due to software bug, be loud */
netdev_err(bp->dev, "null HWRM request");
dump_stack();
return NULL;
}
/* HWRM API has no type safety, verify sentinel to validate address */
sentinel = hwrm_calc_sentinel(ctx, le16_to_cpu(req->req_type));
if (ctx->sentinel != sentinel) {
/* can only be due to software bug, be loud */
netdev_err(bp->dev, "HWRM sentinel mismatch, req_type = %u\n",
(u32)le16_to_cpu(req->req_type));
dump_stack();
return NULL;
}
return ctx;
}
/**
* hwrm_req_timeout() - Set the completion timeout for the request.
* @bp: The driver context.
* @req: The request to set the timeout.
* @timeout: The timeout in milliseconds.
*
* Set the timeout associated with the request for subsequent calls to
* hwrm_req_send(). Some requests are long running and require a different
* timeout than the default.
*/
void hwrm_req_timeout(struct bnxt *bp, void *req, unsigned int timeout)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
if (ctx)
ctx->timeout = timeout;
}
/**
* hwrm_req_alloc_flags() - Sets GFP allocation flags for slices.
* @bp: The driver context.
* @req: The request for which calls to hwrm_req_dma_slice() will have altered
* allocation flags.
* @gfp: A bitmask of GFP flags. These flags are passed to dma_alloc_coherent()
* whenever it is used to allocate backing memory for slices. Note that
* calls to hwrm_req_dma_slice() will not always result in new allocations,
* however, memory suballocated from the request buffer is already
* __GFP_ZERO.
*
* Sets the GFP allocation flags associated with the request for subsequent
* calls to hwrm_req_dma_slice(). This can be useful for specifying __GFP_ZERO
* for slice allocations.
*/
void hwrm_req_alloc_flags(struct bnxt *bp, void *req, gfp_t gfp)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
if (ctx)
ctx->gfp = gfp;
}
/**
* hwrm_req_replace() - Replace request data.
* @bp: The driver context.
* @req: The request to modify. A call to hwrm_req_replace() is conceptually
* an assignment of new_req to req. Subsequent calls to HWRM API functions,
* such as hwrm_req_send(), should thus use req and not new_req (in fact,
* calls to HWRM API functions will fail if non-managed request objects
* are passed).
* @len: The length of new_req.
* @new_req: The pre-built request to copy or reference.
*
* Replaces the request data in req with that of new_req. This is useful in
* scenarios where a request object has already been constructed by a third
* party prior to creating a resource managed request using hwrm_req_init().
* Depending on the length, hwrm_req_replace() will either copy the new
* request data into the DMA memory allocated for req, or it will simply
* reference the new request and use it in lieu of req during subsequent
* calls to hwrm_req_send(). The resource management is associated with
* req and is independent of and does not apply to new_req. The caller must
* ensure that the lifetime of new_req is least as long as req. Any slices
* that may have been associated with the original request are released.
*
* Return: zero on success, negative error code otherwise:
* E2BIG: Request is too large.
* EINVAL: Invalid request to modify.
*/
int hwrm_req_replace(struct bnxt *bp, void *req, void *new_req, u32 len)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
struct input *internal_req = req;
u16 req_type;
if (!ctx)
return -EINVAL;
if (len > BNXT_HWRM_CTX_OFFSET)
return -E2BIG;
/* free any existing slices */
ctx->allocated = BNXT_HWRM_DMA_SIZE - BNXT_HWRM_CTX_OFFSET;
if (ctx->slice_addr) {
dma_free_coherent(&bp->pdev->dev, ctx->slice_size,
ctx->slice_addr, ctx->slice_handle);
ctx->slice_addr = NULL;
}
ctx->gfp = GFP_KERNEL;
if ((bp->fw_cap & BNXT_FW_CAP_SHORT_CMD) || len > BNXT_HWRM_MAX_REQ_LEN) {
memcpy(internal_req, new_req, len);
} else {
internal_req->req_type = ((struct input *)new_req)->req_type;
ctx->req = new_req;
}
ctx->req_len = len;
ctx->req->resp_addr = cpu_to_le64(ctx->dma_handle +
BNXT_HWRM_RESP_OFFSET);
/* update sentinel for potentially new request type */
req_type = le16_to_cpu(internal_req->req_type);
ctx->sentinel = hwrm_calc_sentinel(ctx, req_type);
return 0;
}
/**
* hwrm_req_flags() - Set non internal flags of the ctx
* @bp: The driver context.
* @req: The request containing the HWRM command
* @flags: ctx flags that don't have BNXT_HWRM_INTERNAL_FLAG set
*
* ctx flags can be used by the callers to instruct how the subsequent
* hwrm_req_send() should behave. Example: callers can use hwrm_req_flags
* with BNXT_HWRM_CTX_SILENT to omit kernel prints of errors of hwrm_req_send()
* or with BNXT_HWRM_FULL_WAIT enforce hwrm_req_send() to wait for full timeout
* even if FW is not responding.
* This generic function can be used to set any flag that is not an internal flag
* of the HWRM module.
*/
void hwrm_req_flags(struct bnxt *bp, void *req, enum bnxt_hwrm_ctx_flags flags)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
if (ctx)
ctx->flags |= (flags & HWRM_API_FLAGS);
}
/**
* hwrm_req_hold() - Claim ownership of the request's resources.
* @bp: The driver context.
* @req: A pointer to the request to own. The request will no longer be
* consumed by calls to hwrm_req_send().
*
* Take ownership of the request. Ownership places responsibility on the
* caller to free the resources associated with the request via a call to
* hwrm_req_drop(). The caller taking ownership implies that a subsequent
* call to hwrm_req_send() will not consume the request (ie. sending will
* not free the associated resources if the request is owned by the caller).
* Taking ownership returns a reference to the response. Retaining and
* accessing the response data is the most common reason to take ownership
* of the request. Ownership can also be acquired in order to reuse the same
* request object across multiple invocations of hwrm_req_send().
*
* Return: A pointer to the response object.
*
* The resources associated with the response will remain available to the
* caller until ownership of the request is relinquished via a call to
* hwrm_req_drop(). It is not possible for hwrm_req_hold() to return NULL if
* a valid request is provided. A returned NULL value would imply a driver
* bug and the implementation will complain loudly in the logs to aid in
* detection. It should not be necessary to check the result for NULL.
*/
void *hwrm_req_hold(struct bnxt *bp, void *req)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
struct input *input = (struct input *)req;
if (!ctx)
return NULL;
if (ctx->flags & BNXT_HWRM_INTERNAL_CTX_OWNED) {
/* can only be due to software bug, be loud */
netdev_err(bp->dev, "HWRM context already owned, req_type = %u\n",
(u32)le16_to_cpu(input->req_type));
dump_stack();
return NULL;
}
ctx->flags |= BNXT_HWRM_INTERNAL_CTX_OWNED;
return ((u8 *)req) + BNXT_HWRM_RESP_OFFSET;
}
static void __hwrm_ctx_drop(struct bnxt *bp, struct bnxt_hwrm_ctx *ctx)
{
void *addr = ((u8 *)ctx) - BNXT_HWRM_CTX_OFFSET;
dma_addr_t dma_handle = ctx->dma_handle; /* save before invalidate */
/* unmap any auxiliary DMA slice */
if (ctx->slice_addr)
dma_free_coherent(&bp->pdev->dev, ctx->slice_size,
ctx->slice_addr, ctx->slice_handle);
/* invalidate, ensure ownership, sentinel and dma_handle are cleared */
memset(ctx, 0, sizeof(struct bnxt_hwrm_ctx));
/* return the buffer to the DMA pool */
if (dma_handle)
dma_pool_free(bp->hwrm_dma_pool, addr, dma_handle);
}
/**
* hwrm_req_drop() - Release all resources associated with the request.
* @bp: The driver context.
* @req: The request to consume, releasing the associated resources. The
* request object, any slices, and its associated response are no
* longer valid.
*
* It is legal to call hwrm_req_drop() on an unowned request, provided it
* has not already been consumed by hwrm_req_send() (for example, to release
* an aborted request). A given request should not be dropped more than once,
* nor should it be dropped after having been consumed by hwrm_req_send(). To
* do so is an error (the context will not be found and a stack trace will be
* rendered in the kernel log).
*/
void hwrm_req_drop(struct bnxt *bp, void *req)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
if (ctx)
__hwrm_ctx_drop(bp, ctx);
}
static int __hwrm_to_stderr(u32 hwrm_err)
{
switch (hwrm_err) {
case HWRM_ERR_CODE_SUCCESS:
return 0;
case HWRM_ERR_CODE_RESOURCE_LOCKED:
return -EROFS;
case HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED:
return -EACCES;
case HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR:
return -ENOSPC;
case HWRM_ERR_CODE_INVALID_PARAMS:
case HWRM_ERR_CODE_INVALID_FLAGS:
case HWRM_ERR_CODE_INVALID_ENABLES:
case HWRM_ERR_CODE_UNSUPPORTED_TLV:
case HWRM_ERR_CODE_UNSUPPORTED_OPTION_ERR:
return -EINVAL;
case HWRM_ERR_CODE_NO_BUFFER:
return -ENOMEM;
case HWRM_ERR_CODE_HOT_RESET_PROGRESS:
case HWRM_ERR_CODE_BUSY:
return -EAGAIN;
case HWRM_ERR_CODE_CMD_NOT_SUPPORTED:
return -EOPNOTSUPP;
case HWRM_ERR_CODE_PF_UNAVAILABLE:
return -ENODEV;
default:
return -EIO;
}
}
static struct bnxt_hwrm_wait_token *
__hwrm_acquire_token(struct bnxt *bp, enum bnxt_hwrm_chnl dst)
{
struct bnxt_hwrm_wait_token *token;
token = kzalloc_obj(*token);
if (!token)
return NULL;
mutex_lock(&bp->hwrm_cmd_lock);
token->dst = dst;
token->state = BNXT_HWRM_PENDING;
if (dst == BNXT_HWRM_CHNL_CHIMP) {
token->seq_id = bp->hwrm_cmd_seq++;
hlist_add_head_rcu(&token->node, &bp->hwrm_pending_list);
} else {
token->seq_id = bp->hwrm_cmd_kong_seq++;
}
return token;
}
static void
__hwrm_release_token(struct bnxt *bp, struct bnxt_hwrm_wait_token *token)
{
if (token->dst == BNXT_HWRM_CHNL_CHIMP) {
hlist_del_rcu(&token->node);
kfree_rcu(token, rcu);
} else {
kfree(token);
}
mutex_unlock(&bp->hwrm_cmd_lock);
}
void
hwrm_update_token(struct bnxt *bp, u16 seq_id, enum bnxt_hwrm_wait_state state)
{
struct bnxt_hwrm_wait_token *token;
rcu_read_lock();
hlist_for_each_entry_rcu(token, &bp->hwrm_pending_list, node) {
if (token->seq_id == seq_id) {
WRITE_ONCE(token->state, state);
rcu_read_unlock();
return;
}
}
rcu_read_unlock();
netdev_err(bp->dev, "Invalid hwrm seq id %d\n", seq_id);
}
static void hwrm_req_dbg(struct bnxt *bp, struct input *req)
{
u32 ring = le16_to_cpu(req->cmpl_ring);
u32 type = le16_to_cpu(req->req_type);
u32 tgt = le16_to_cpu(req->target_id);
u32 seq = le16_to_cpu(req->seq_id);
char opt[32] = "\n";
if (unlikely(ring != (u16)BNXT_HWRM_NO_CMPL_RING))
snprintf(opt, 16, " ring %d\n", ring);
if (unlikely(tgt != BNXT_HWRM_TARGET))
snprintf(opt + strlen(opt) - 1, 16, " tgt 0x%x\n", tgt);
netdev_dbg(bp->dev, "sent hwrm req_type 0x%x seq id 0x%x%s",
type, seq, opt);
}
#define hwrm_err(bp, ctx, fmt, ...) \
do { \
if ((ctx)->flags & BNXT_HWRM_CTX_SILENT) \
netdev_dbg((bp)->dev, fmt, __VA_ARGS__); \
else \
netdev_err((bp)->dev, fmt, __VA_ARGS__); \
} while (0)
static bool hwrm_wait_must_abort(struct bnxt *bp, u32 req_type, u32 *fw_status)
{
if (req_type == HWRM_VER_GET)
return false;
if (!bp->fw_health || !bp->fw_health->status_reliable)
return false;
*fw_status = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
return *fw_status && !BNXT_FW_IS_HEALTHY(*fw_status);
}
static int __hwrm_send(struct bnxt *bp, struct bnxt_hwrm_ctx *ctx)
{
u32 doorbell_offset = BNXT_GRCPF_REG_CHIMP_COMM_TRIGGER;
enum bnxt_hwrm_chnl dst = BNXT_HWRM_CHNL_CHIMP;
u32 bar_offset = BNXT_GRCPF_REG_CHIMP_COMM;
struct bnxt_hwrm_wait_token *token = NULL;
struct hwrm_short_input short_input = {0};
u16 max_req_len = BNXT_HWRM_MAX_REQ_LEN;
unsigned int i, timeout, tmo_count;
u32 *data = (u32 *)ctx->req;
u32 msg_len = ctx->req_len;
u32 req_type, sts;
int rc = -EBUSY;
u16 len = 0;
u8 *valid;
if (ctx->flags & BNXT_HWRM_INTERNAL_RESP_DIRTY)
memset(ctx->resp, 0, PAGE_SIZE);
req_type = le16_to_cpu(ctx->req->req_type);
if (BNXT_NO_FW_ACCESS(bp) &&
(req_type != HWRM_FUNC_RESET && req_type != HWRM_VER_GET)) {
netdev_dbg(bp->dev, "hwrm req_type 0x%x skipped, FW channel down\n",
req_type);
goto exit;
}
if (msg_len > BNXT_HWRM_MAX_REQ_LEN &&
msg_len > bp->hwrm_max_ext_req_len) {
netdev_warn(bp->dev, "oversized hwrm request, req_type 0x%x",
req_type);
rc = -E2BIG;
goto exit;
}
if (bnxt_kong_hwrm_message(bp, ctx->req)) {
dst = BNXT_HWRM_CHNL_KONG;
bar_offset = BNXT_GRCPF_REG_KONG_COMM;
doorbell_offset = BNXT_GRCPF_REG_KONG_COMM_TRIGGER;
if (le16_to_cpu(ctx->req->cmpl_ring) != INVALID_HW_RING_ID) {
netdev_err(bp->dev, "Ring completions not supported for KONG commands, req_type = %d\n",
req_type);
rc = -EINVAL;
goto exit;
}
}
token = __hwrm_acquire_token(bp, dst);
if (!token) {
rc = -ENOMEM;
goto exit;
}
ctx->req->seq_id = cpu_to_le16(token->seq_id);
if ((bp->fw_cap & BNXT_FW_CAP_SHORT_CMD) ||
msg_len > BNXT_HWRM_MAX_REQ_LEN) {
short_input.req_type = ctx->req->req_type;
short_input.signature =
cpu_to_le16(SHORT_REQ_SIGNATURE_SHORT_CMD);
short_input.size = cpu_to_le16(msg_len);
short_input.req_addr = cpu_to_le64(ctx->dma_handle);
data = (u32 *)&short_input;
msg_len = sizeof(short_input);
max_req_len = BNXT_HWRM_SHORT_REQ_LEN;
}
/* Ensure any associated DMA buffers are written before doorbell */
wmb();
/* Write request msg to hwrm channel */
__iowrite32_copy(bp->bar0 + bar_offset, data, msg_len / 4);
for (i = msg_len; i < max_req_len; i += 4)
writel(0, bp->bar0 + bar_offset + i);
/* Ring channel doorbell */
writel(1, bp->bar0 + doorbell_offset);
hwrm_req_dbg(bp, ctx->req);
if (!pci_is_enabled(bp->pdev)) {
rc = -ENODEV;
goto exit;
}
/* Limit timeout to an upper limit */
timeout = min(ctx->timeout, bp->hwrm_cmd_max_timeout ?: HWRM_CMD_MAX_TIMEOUT);
/* convert timeout to usec */
timeout *= 1000;
i = 0;
/* Short timeout for the first few iterations:
* number of loops = number of loops for short timeout +
* number of loops for standard timeout.
*/
tmo_count = HWRM_SHORT_TIMEOUT_COUNTER;
timeout = timeout - HWRM_SHORT_MIN_TIMEOUT * HWRM_SHORT_TIMEOUT_COUNTER;
tmo_count += DIV_ROUND_UP(timeout, HWRM_MIN_TIMEOUT);
if (le16_to_cpu(ctx->req->cmpl_ring) != INVALID_HW_RING_ID) {
/* Wait until hwrm response cmpl interrupt is processed */
while (READ_ONCE(token->state) < BNXT_HWRM_COMPLETE &&
i++ < tmo_count) {
/* Abort the wait for completion if the FW health
* check has failed.
*/
if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))
goto exit;
/* on first few passes, just barely sleep */
if (i < HWRM_SHORT_TIMEOUT_COUNTER) {
usleep_range(HWRM_SHORT_MIN_TIMEOUT,
HWRM_SHORT_MAX_TIMEOUT);
} else {
if (hwrm_wait_must_abort(bp, req_type, &sts)) {
hwrm_err(bp, ctx, "Resp cmpl intr abandoning msg: 0x%x due to firmware status: 0x%x\n",
req_type, sts);
goto exit;
}
usleep_range(HWRM_MIN_TIMEOUT,
HWRM_MAX_TIMEOUT);
}
}
if (READ_ONCE(token->state) != BNXT_HWRM_COMPLETE) {
hwrm_err(bp, ctx, "Resp cmpl intr err msg: 0x%x\n",
req_type);
goto exit;
}
len = le16_to_cpu(READ_ONCE(ctx->resp->resp_len));
valid = ((u8 *)ctx->resp) + len - 1;
} else {
__le16 seen_out_of_seq = ctx->req->seq_id; /* will never see */
int j;
/* Check if response len is updated */
for (i = 0; i < tmo_count; i++) {
/* Abort the wait for completion if the FW health
* check has failed.
*/
if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))
goto exit;
if (token &&
READ_ONCE(token->state) == BNXT_HWRM_DEFERRED) {
__hwrm_release_token(bp, token);
token = NULL;
}
len = le16_to_cpu(READ_ONCE(ctx->resp->resp_len));
if (len) {
__le16 resp_seq = READ_ONCE(ctx->resp->seq_id);
if (resp_seq == ctx->req->seq_id)
break;
if (resp_seq != seen_out_of_seq) {
netdev_warn(bp->dev, "Discarding out of seq response: 0x%x for msg {0x%x 0x%x}\n",
le16_to_cpu(resp_seq),
req_type,
le16_to_cpu(ctx->req->seq_id));
seen_out_of_seq = resp_seq;
}
}
/* on first few passes, just barely sleep */
if (i < HWRM_SHORT_TIMEOUT_COUNTER) {
usleep_range(HWRM_SHORT_MIN_TIMEOUT,
HWRM_SHORT_MAX_TIMEOUT);
} else {
if (hwrm_wait_must_abort(bp, req_type, &sts)) {
hwrm_err(bp, ctx, "Abandoning msg {0x%x 0x%x} len: %d due to firmware status: 0x%x\n",
req_type,
le16_to_cpu(ctx->req->seq_id),
len, sts);
goto exit;
}
usleep_range(HWRM_MIN_TIMEOUT,
HWRM_MAX_TIMEOUT);
}
}
if (i >= tmo_count) {
hwrm_err(bp, ctx, "Error (timeout: %u) msg {0x%x 0x%x} len:%d\n",
hwrm_total_timeout(i), req_type,
le16_to_cpu(ctx->req->seq_id), len);
goto exit;
}
/* Last byte of resp contains valid bit */
valid = ((u8 *)ctx->resp) + len - 1;
for (j = 0; j < HWRM_VALID_BIT_DELAY_USEC; ) {
/* make sure we read from updated DMA memory */
dma_rmb();
if (*valid)
break;
if (j < 10) {
udelay(1);
j++;
} else {
usleep_range(20, 30);
j += 20;
}
}
if (j >= HWRM_VALID_BIT_DELAY_USEC) {
hwrm_err(bp, ctx, "Error (timeout: %u) msg {0x%x 0x%x} len:%d v:%d\n",
hwrm_total_timeout(i) + j, req_type,
le16_to_cpu(ctx->req->seq_id), len, *valid);
goto exit;
}
}
/* Zero valid bit for compatibility. Valid bit in an older spec
* may become a new field in a newer spec. We must make sure that
* a new field not implemented by old spec will read zero.
*/
*valid = 0;
rc = le16_to_cpu(ctx->resp->error_code);
if (rc == HWRM_ERR_CODE_BUSY && !(ctx->flags & BNXT_HWRM_CTX_SILENT))
netdev_warn(bp->dev, "FW returned busy, hwrm req_type 0x%x\n",
req_type);
else if (rc && rc != HWRM_ERR_CODE_PF_UNAVAILABLE)
hwrm_err(bp, ctx, "hwrm req_type 0x%x seq id 0x%x error 0x%x\n",
req_type, le16_to_cpu(ctx->req->seq_id), rc);
rc = __hwrm_to_stderr(rc);
exit:
if (token)
__hwrm_release_token(bp, token);
if (ctx->flags & BNXT_HWRM_INTERNAL_CTX_OWNED)
ctx->flags |= BNXT_HWRM_INTERNAL_RESP_DIRTY;
else
__hwrm_ctx_drop(bp, ctx);
return rc;
}
/**
* hwrm_req_send() - Execute an HWRM command.
* @bp: The driver context.
* @req: A pointer to the request to send. The DMA resources associated with
* the request will be released (ie. the request will be consumed) unless
* ownership of the request has been assumed by the caller via a call to
* hwrm_req_hold().
*
* Send an HWRM request to the device and wait for a response. The request is
* consumed if it is not owned by the caller. This function will block until
* the request has either completed or times out due to an error.
*
* Return: A result code.
*
* The result is zero on success, otherwise the negative error code indicates
* one of the following errors:
* E2BIG: The request was too large.
* EBUSY: The firmware is in a fatal state or the request timed out
* EACCESS: HWRM access denied.
* ENOSPC: HWRM resource allocation error.
* EINVAL: Request parameters are invalid.
* ENOMEM: HWRM has no buffers.
* EAGAIN: HWRM busy or reset in progress.
* EOPNOTSUPP: Invalid request type.
* EIO: Any other error.
* Error handling is orthogonal to request ownership. An unowned request will
* still be consumed on error. If the caller owns the request, then the caller
* is responsible for releasing the resources. Otherwise, hwrm_req_send() will
* always consume the request.
*/
int hwrm_req_send(struct bnxt *bp, void *req)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
if (!ctx)
return -EINVAL;
return __hwrm_send(bp, ctx);
}
/**
* hwrm_req_send_silent() - A silent version of hwrm_req_send().
* @bp: The driver context.
* @req: The request to send without logging.
*
* The same as hwrm_req_send(), except that the request is silenced using
* hwrm_req_silence() prior the call. This version of the function is
* provided solely to preserve the legacy API’s flavor for this functionality.
*
* Return: A result code, see hwrm_req_send().
*/
int hwrm_req_send_silent(struct bnxt *bp, void *req)
{
hwrm_req_flags(bp, req, BNXT_HWRM_CTX_SILENT);
return hwrm_req_send(bp, req);
}
/**
* hwrm_req_dma_slice() - Allocate a slice of DMA mapped memory.
* @bp: The driver context.
* @req: The request for which indirect data will be associated.
* @size: The size of the allocation.
* @dma_handle: The bus address associated with the allocation. The HWRM API has
* no knowledge about the type of the request and so cannot infer how the
* caller intends to use the indirect data. Thus, the caller is
* responsible for configuring the request object appropriately to
* point to the associated indirect memory. Note, DMA handle has the
* same definition as it does in dma_alloc_coherent(), the caller is
* responsible for endian conversions via cpu_to_le64() before assigning
* this address.
*
* Allocates DMA mapped memory for indirect data related to a request. The
* lifetime of the DMA resources will be bound to that of the request (ie.
* they will be automatically released when the request is either consumed by
* hwrm_req_send() or dropped by hwrm_req_drop()). Small allocations are
* efficiently suballocated out of the request buffer space, hence the name
* slice, while larger requests are satisfied via an underlying call to
* dma_alloc_coherent(). Multiple suballocations are supported, however, only
* one externally mapped region is.
*
* Return: The kernel virtual address of the DMA mapping.
*/
void *
hwrm_req_dma_slice(struct bnxt *bp, void *req, u32 size, dma_addr_t *dma_handle)
{
struct bnxt_hwrm_ctx *ctx = __hwrm_ctx(bp, req);
u8 *end = ((u8 *)req) + BNXT_HWRM_DMA_SIZE;
struct input *input = req;
u8 *addr, *req_addr = req;
u32 max_offset, offset;
if (!ctx)
return NULL;
max_offset = BNXT_HWRM_DMA_SIZE - ctx->allocated;
offset = max_offset - size;
offset = ALIGN_DOWN(offset, BNXT_HWRM_DMA_ALIGN);
addr = req_addr + offset;
if (addr < req_addr + max_offset && req_addr + ctx->req_len <= addr) {
ctx->allocated = end - addr;
*dma_handle = ctx->dma_handle + offset;
return addr;
}
/* could not suballocate from ctx buffer, try create a new mapping */
if (ctx->slice_addr) {
/* if one exists, can only be due to software bug, be loud */
netdev_err(bp->dev, "HWRM refusing to reallocate DMA slice, req_type = %u\n",
(u32)le16_to_cpu(input->req_type));
dump_stack();
return NULL;
}
addr = dma_alloc_coherent(&bp->pdev->dev, size, dma_handle, ctx->gfp);
if (!addr)
return NULL;
ctx->slice_addr = addr;
ctx->slice_size = size;
ctx->slice_handle = *dma_handle;
return addr;
}
@@ -0,0 +1,154 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2020 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_HWRM_H
#define BNXT_HWRM_H
#include <linux/bnxt/hsi.h>
enum bnxt_hwrm_ctx_flags {
/* Update the HWRM_API_FLAGS right below for any new non-internal bit added here */
BNXT_HWRM_INTERNAL_CTX_OWNED = BIT(0), /* caller owns the context */
BNXT_HWRM_INTERNAL_RESP_DIRTY = BIT(1), /* response contains data */
BNXT_HWRM_CTX_SILENT = BIT(2), /* squelch firmware errors */
BNXT_HWRM_FULL_WAIT = BIT(3), /* wait for full timeout of HWRM command */
};
#define HWRM_API_FLAGS (BNXT_HWRM_CTX_SILENT | BNXT_HWRM_FULL_WAIT)
struct bnxt_hwrm_ctx {
u64 sentinel;
dma_addr_t dma_handle;
struct output *resp;
struct input *req;
dma_addr_t slice_handle;
void *slice_addr;
u32 slice_size;
u32 req_len;
enum bnxt_hwrm_ctx_flags flags;
unsigned int timeout;
u32 allocated;
gfp_t gfp;
};
enum bnxt_hwrm_wait_state {
BNXT_HWRM_PENDING,
BNXT_HWRM_DEFERRED,
BNXT_HWRM_COMPLETE,
BNXT_HWRM_CANCELLED,
};
enum bnxt_hwrm_chnl { BNXT_HWRM_CHNL_CHIMP, BNXT_HWRM_CHNL_KONG };
struct bnxt_hwrm_wait_token {
struct rcu_head rcu;
struct hlist_node node;
enum bnxt_hwrm_wait_state state;
enum bnxt_hwrm_chnl dst;
u16 seq_id;
};
void hwrm_update_token(struct bnxt *bp, u16 seq, enum bnxt_hwrm_wait_state s);
#define BNXT_HWRM_MAX_REQ_LEN (bp->hwrm_max_req_len)
#define BNXT_HWRM_SHORT_REQ_LEN sizeof(struct hwrm_short_input)
#define HWRM_CMD_MAX_TIMEOUT 60000U
#define SHORT_HWRM_CMD_TIMEOUT 20
#define HWRM_CMD_TIMEOUT (bp->hwrm_cmd_timeout)
#define HWRM_RESET_TIMEOUT ((HWRM_CMD_TIMEOUT) * 4)
#define BNXT_HWRM_TARGET 0xffff
#define BNXT_HWRM_NO_CMPL_RING -1
#define BNXT_HWRM_REQ_MAX_SIZE 128
#define BNXT_HWRM_DMA_SIZE (2 * PAGE_SIZE) /* space for req+resp */
#define BNXT_HWRM_RESP_RESERVED PAGE_SIZE
#define BNXT_HWRM_RESP_OFFSET (BNXT_HWRM_DMA_SIZE - \
BNXT_HWRM_RESP_RESERVED)
#define BNXT_HWRM_CTX_OFFSET (BNXT_HWRM_RESP_OFFSET - \
sizeof(struct bnxt_hwrm_ctx))
#define BNXT_HWRM_DMA_ALIGN 16
#define BNXT_HWRM_SENTINEL 0xb6e1f68a12e9a7eb /* arbitrary value */
#define BNXT_HWRM_REQS_PER_PAGE (BNXT_PAGE_SIZE / \
BNXT_HWRM_REQ_MAX_SIZE)
#define HWRM_SHORT_MIN_TIMEOUT 3
#define HWRM_SHORT_MAX_TIMEOUT 10
#define HWRM_SHORT_TIMEOUT_COUNTER 5
#define HWRM_MIN_TIMEOUT 25
#define HWRM_MAX_TIMEOUT 40
static inline unsigned int hwrm_total_timeout(unsigned int n)
{
return n <= HWRM_SHORT_TIMEOUT_COUNTER ? n * HWRM_SHORT_MIN_TIMEOUT :
HWRM_SHORT_TIMEOUT_COUNTER * HWRM_SHORT_MIN_TIMEOUT +
(n - HWRM_SHORT_TIMEOUT_COUNTER) * HWRM_MIN_TIMEOUT;
}
#define HWRM_VALID_BIT_DELAY_USEC 50000
static inline bool bnxt_cfa_hwrm_message(u16 req_type)
{
switch (req_type) {
case HWRM_CFA_ENCAP_RECORD_ALLOC:
case HWRM_CFA_ENCAP_RECORD_FREE:
case HWRM_CFA_DECAP_FILTER_ALLOC:
case HWRM_CFA_DECAP_FILTER_FREE:
case HWRM_CFA_EM_FLOW_ALLOC:
case HWRM_CFA_EM_FLOW_FREE:
case HWRM_CFA_EM_FLOW_CFG:
case HWRM_CFA_FLOW_ALLOC:
case HWRM_CFA_FLOW_FREE:
case HWRM_CFA_FLOW_INFO:
case HWRM_CFA_FLOW_FLUSH:
case HWRM_CFA_FLOW_STATS:
case HWRM_CFA_METER_PROFILE_ALLOC:
case HWRM_CFA_METER_PROFILE_FREE:
case HWRM_CFA_METER_PROFILE_CFG:
case HWRM_CFA_METER_INSTANCE_ALLOC:
case HWRM_CFA_METER_INSTANCE_FREE:
return true;
default:
return false;
}
}
static inline bool bnxt_kong_hwrm_message(struct bnxt *bp, struct input *req)
{
return (bp->fw_cap & BNXT_FW_CAP_KONG_MB_CHNL &&
(bnxt_cfa_hwrm_message(le16_to_cpu(req->req_type)) ||
le16_to_cpu(req->target_id) == HWRM_TARGET_ID_KONG));
}
int __hwrm_req_init(struct bnxt *bp, void **req, u16 req_type, u32 req_len);
#define hwrm_req_init(bp, req, req_type) \
__hwrm_req_init((bp), (void **)&(req), (req_type), sizeof(*(req)))
void *hwrm_req_hold(struct bnxt *bp, void *req);
void hwrm_req_drop(struct bnxt *bp, void *req);
void hwrm_req_flags(struct bnxt *bp, void *req, enum bnxt_hwrm_ctx_flags flags);
void hwrm_req_timeout(struct bnxt *bp, void *req, unsigned int timeout);
int hwrm_req_send(struct bnxt *bp, void *req);
int hwrm_req_send_silent(struct bnxt *bp, void *req);
int hwrm_req_replace(struct bnxt *bp, void *req, void *new_req, u32 len);
void hwrm_req_alloc_flags(struct bnxt *bp, void *req, gfp_t flags);
void *hwrm_req_dma_slice(struct bnxt *bp, void *req, u32 size, dma_addr_t *dma);
/* Older devices can only support req length of 128.
* HWRM_FUNC_CFG requests which don't need fields starting at
* num_quic_tx_key_ctxs can use this helper to avoid getting -E2BIG.
*/
static inline int
bnxt_hwrm_func_cfg_short_req_init(struct bnxt *bp,
struct hwrm_func_cfg_input **req)
{
u32 req_len;
req_len = min_t(u32, sizeof(**req), bp->hwrm_max_ext_req_len);
return __hwrm_req_init(bp, (void **)req, HWRM_FUNC_CFG, req_len);
}
#endif
@@ -0,0 +1,72 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef _BNXT_NVM_DEFS_H_
#define _BNXT_NVM_DEFS_H_
enum bnxt_nvm_directory_type {
BNX_DIR_TYPE_UNUSED = 0,
BNX_DIR_TYPE_PKG_LOG = 1,
BNX_DIR_TYPE_UPDATE = 2,
BNX_DIR_TYPE_CHIMP_PATCH = 3,
BNX_DIR_TYPE_BOOTCODE = 4,
BNX_DIR_TYPE_VPD = 5,
BNX_DIR_TYPE_EXP_ROM_MBA = 6,
BNX_DIR_TYPE_AVS = 7,
BNX_DIR_TYPE_PCIE = 8,
BNX_DIR_TYPE_PORT_MACRO = 9,
BNX_DIR_TYPE_APE_FW = 10,
BNX_DIR_TYPE_APE_PATCH = 11,
BNX_DIR_TYPE_KONG_FW = 12,
BNX_DIR_TYPE_KONG_PATCH = 13,
BNX_DIR_TYPE_BONO_FW = 14,
BNX_DIR_TYPE_BONO_PATCH = 15,
BNX_DIR_TYPE_TANG_FW = 16,
BNX_DIR_TYPE_TANG_PATCH = 17,
BNX_DIR_TYPE_BOOTCODE_2 = 18,
BNX_DIR_TYPE_CCM = 19,
BNX_DIR_TYPE_PCI_CFG = 20,
BNX_DIR_TYPE_TSCF_UCODE = 21,
BNX_DIR_TYPE_ISCSI_BOOT = 22,
BNX_DIR_TYPE_ISCSI_BOOT_IPV6 = 24,
BNX_DIR_TYPE_ISCSI_BOOT_IPV4N6 = 25,
BNX_DIR_TYPE_ISCSI_BOOT_CFG6 = 26,
BNX_DIR_TYPE_EXT_PHY = 27,
BNX_DIR_TYPE_SHARED_CFG = 40,
BNX_DIR_TYPE_PORT_CFG = 41,
BNX_DIR_TYPE_FUNC_CFG = 42,
BNX_DIR_TYPE_MGMT_CFG = 48,
BNX_DIR_TYPE_MGMT_DATA = 49,
BNX_DIR_TYPE_MGMT_WEB_DATA = 50,
BNX_DIR_TYPE_MGMT_WEB_META = 51,
BNX_DIR_TYPE_MGMT_EVENT_LOG = 52,
BNX_DIR_TYPE_MGMT_AUDIT_LOG = 53
};
#define BNX_DIR_ORDINAL_FIRST 0
#define BNX_DIR_EXT_NONE 0
#define BNX_DIR_EXT_INACTIVE (1 << 0)
#define BNX_DIR_EXT_UPDATE (1 << 1)
#define BNX_DIR_ATTR_NONE 0
#define BNX_DIR_ATTR_NO_CHKSUM (1 << 0)
#define BNX_DIR_ATTR_PROP_STREAM (1 << 1)
enum bnxnvm_pkglog_field_index {
BNX_PKG_LOG_FIELD_IDX_INSTALLED_TIMESTAMP = 0,
BNX_PKG_LOG_FIELD_IDX_PKG_DESCRIPTION = 1,
BNX_PKG_LOG_FIELD_IDX_PKG_VERSION = 2,
BNX_PKG_LOG_FIELD_IDX_PKG_TIMESTAMP = 3,
BNX_PKG_LOG_FIELD_IDX_PKG_CHECKSUM = 4,
BNX_PKG_LOG_FIELD_IDX_INSTALLED_ITEMS = 5,
BNX_PKG_LOG_FIELD_IDX_INSTALLED_MASK = 6
};
#endif /* Don't add anything after this line */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,201 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2021 Broadcom Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_PTP_H
#define BNXT_PTP_H
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
#define BNXT_PTP_GRC_WIN 6
#define BNXT_PTP_GRC_WIN_BASE 0x6000
#define BNXT_MAX_PHC_DRIFT 31000000
#define BNXT_CYCLES_SHIFT 23
#define BNXT_DEVCLK_FREQ 1000000
#define BNXT_LO_TIMER_MASK 0x0000ffffffffUL
#define BNXT_HI_TIMER_MASK 0xffff00000000UL
#define BNXT_HI_TIMER_SHIFT 24
#define BNXT_PTP_DFLT_TX_TMO 1000 /* ms */
#define BNXT_PTP_QTS_TIMEOUT 1000
#define BNXT_PTP_QTS_MAX_TMO_US 65535U
#define BNXT_PTP_QTS_TX_ENABLES (PORT_TS_QUERY_REQ_ENABLES_PTP_SEQ_ID | \
PORT_TS_QUERY_REQ_ENABLES_TS_REQ_TIMEOUT | \
PORT_TS_QUERY_REQ_ENABLES_PTP_HDR_OFFSET)
struct pps_pin {
u8 event;
u8 usage;
u8 state;
};
#define TSIO_PIN_VALID(pin) ((pin) >= 0 && (pin) < (BNXT_MAX_TSIO_PINS))
#define EVENT_DATA2_PPS_EVENT_TYPE(data2) \
((data2) & ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA2_EVENT_TYPE)
#define EVENT_DATA2_PPS_PIN_NUM(data2) \
(((data2) & \
ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA2_PIN_NUMBER_MASK) >>\
ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA2_PIN_NUMBER_SFT)
#define BNXT_DATA2_UPPER_MSK \
ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA2_PPS_TIMESTAMP_UPPER_MASK
#define BNXT_DATA2_UPPER_SFT \
(32 - \
ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA2_PPS_TIMESTAMP_UPPER_SFT)
#define BNXT_DATA1_LOWER_MSK \
ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA1_PPS_TIMESTAMP_LOWER_MASK
#define BNXT_DATA1_LOWER_SFT \
ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA1_PPS_TIMESTAMP_LOWER_SFT
#define EVENT_PPS_TS(data2, data1) \
(((u64)((data2) & BNXT_DATA2_UPPER_MSK) << BNXT_DATA2_UPPER_SFT) |\
(((data1) & BNXT_DATA1_LOWER_MSK) >> BNXT_DATA1_LOWER_SFT))
#define BNXT_PPS_PIN_DISABLE 0
#define BNXT_PPS_PIN_ENABLE 1
#define BNXT_PPS_PIN_NONE 0
#define BNXT_PPS_PIN_PPS_IN 1
#define BNXT_PPS_PIN_PPS_OUT 2
#define BNXT_PPS_PIN_SYNC_IN 3
#define BNXT_PPS_PIN_SYNC_OUT 4
#define BNXT_PPS_EVENT_INTERNAL 1
#define BNXT_PPS_EVENT_EXTERNAL 2
struct bnxt_pps {
u8 num_pins;
#define BNXT_MAX_TSIO_PINS 4
struct pps_pin pins[BNXT_MAX_TSIO_PINS];
};
struct bnxt_ptp_stats {
u64 ts_pkts;
u64 ts_lost;
atomic64_t ts_err;
};
#define BNXT_MAX_TX_TS 4
#define NEXT_TXTS(idx) (((idx) + 1) & (BNXT_MAX_TX_TS - 1))
struct bnxt_ptp_tx_req {
struct sk_buff *tx_skb;
u16 tx_seqid;
u16 tx_hdr_off;
unsigned long abs_txts_tmo;
};
struct bnxt_ptp_cfg {
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp_clock;
struct cyclecounter cc;
struct timecounter tc;
struct bnxt_pps pps_info;
/* serialize timecounter access */
seqlock_t ptp_lock;
/* serialize ts tx request queuing */
spinlock_t ptp_tx_lock;
u64 current_time;
unsigned long next_period;
unsigned long next_overflow_check;
u32 cmult;
/* cache of upper 24 bits of cyclecoutner. 8 bits are used to check for roll-over */
u32 old_time;
/* a 23b shift cyclecounter will overflow in ~36 mins. Check overflow every 18 mins. */
#define BNXT_PHC_OVERFLOW_PERIOD (18 * 60 * HZ)
struct bnxt_ptp_tx_req txts_req[BNXT_MAX_TX_TS];
struct bnxt *bp;
u32 tx_avail;
u16 rxctl;
#define BNXT_PTP_MSG_SYNC (1 << 0)
#define BNXT_PTP_MSG_DELAY_REQ (1 << 1)
#define BNXT_PTP_MSG_PDELAY_REQ (1 << 2)
#define BNXT_PTP_MSG_PDELAY_RESP (1 << 3)
#define BNXT_PTP_MSG_FOLLOW_UP (1 << 8)
#define BNXT_PTP_MSG_DELAY_RESP (1 << 9)
#define BNXT_PTP_MSG_PDELAY_RESP_FOLLOW_UP (1 << 10)
#define BNXT_PTP_MSG_ANNOUNCE (1 << 11)
#define BNXT_PTP_MSG_SIGNALING (1 << 12)
#define BNXT_PTP_MSG_MANAGEMENT (1 << 13)
#define BNXT_PTP_MSG_EVENTS (BNXT_PTP_MSG_SYNC | \
BNXT_PTP_MSG_DELAY_REQ | \
BNXT_PTP_MSG_PDELAY_REQ | \
BNXT_PTP_MSG_PDELAY_RESP)
u8 tx_tstamp_en:1;
u8 rtc_configured:1;
int rx_filter;
u32 tstamp_filters;
u32 refclk_regs[2];
u32 refclk_mapped_regs[2];
u32 txts_tmo;
u16 txts_prod;
u16 txts_cons;
struct bnxt_ptp_stats stats;
};
#define BNXT_PTP_INC_TX_AVAIL(ptp) \
do { \
spin_lock_bh(&(ptp)->ptp_tx_lock); \
(ptp)->tx_avail++; \
spin_unlock_bh(&(ptp)->ptp_tx_lock); \
} while (0)
int bnxt_ptp_parse(struct sk_buff *skb, u16 *seq_id, u16 *hdr_off);
void bnxt_ptp_update_current_time(struct bnxt *bp);
void bnxt_ptp_pps_event(struct bnxt *bp, u32 data1, u32 data2);
int bnxt_ptp_cfg_tstamp_filters(struct bnxt *bp);
void bnxt_ptp_reapply_pps(struct bnxt *bp);
int bnxt_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf,
struct netlink_ext_ack *extack);
int bnxt_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf);
void bnxt_ptp_free_txts_skbs(struct bnxt_ptp_cfg *ptp);
int bnxt_ptp_get_txts_prod(struct bnxt_ptp_cfg *ptp, u16 *prod);
void bnxt_get_tx_ts_p5(struct bnxt *bp, struct sk_buff *skb, u16 prod);
int bnxt_get_rx_ts_p5(struct bnxt *bp, u64 *ts, u32 pkt_ts);
void bnxt_tx_ts_cmp(struct bnxt *bp, struct bnxt_napi *bnapi,
struct tx_ts_cmp *tscmp);
void bnxt_ptp_rtc_timecounter_init(struct bnxt_ptp_cfg *ptp, u64 ns);
int bnxt_ptp_init_rtc(struct bnxt *bp, bool phc_cfg);
int bnxt_ptp_init(struct bnxt *bp);
void bnxt_ptp_clear(struct bnxt *bp);
static inline u64 bnxt_timecounter_cyc2time(struct bnxt_ptp_cfg *ptp, u64 ts)
{
unsigned int seq;
u64 ns;
do {
seq = read_seqbegin(&ptp->ptp_lock);
ns = timecounter_cyc2time(&ptp->tc, ts);
} while (read_seqretry(&ptp->ptp_lock, seq));
return ns;
}
static inline u64 bnxt_extend_cycles_32b_to_48b(struct bnxt_ptp_cfg *ptp, u32 ts)
{
u64 time, cycles;
time = (u64)READ_ONCE(ptp->old_time) << BNXT_HI_TIMER_SHIFT;
cycles = (time & BNXT_HI_TIMER_MASK) | ts;
if (ts < (time & BNXT_LO_TIMER_MASK))
cycles += BNXT_LO_TIMER_MASK + 1;
return cycles;
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
* Copyright (c) 2016-2018 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_SRIOV_H
#define BNXT_SRIOV_H
#define BNXT_FWD_RESP_SIZE_ERR(n) \
((offsetof(struct hwrm_fwd_resp_input, encap_resp) + n) > \
sizeof(struct hwrm_fwd_resp_input))
#define BNXT_EXEC_FWD_RESP_SIZE_ERR(n) \
((offsetof(struct hwrm_exec_fwd_resp_input, encap_request) + n) >\
offsetof(struct hwrm_exec_fwd_resp_input, encap_resp_target_id))
#define BNXT_REJ_FWD_RESP_SIZE_ERR(n) \
((offsetof(struct hwrm_reject_fwd_resp_input, encap_request) + n) >\
offsetof(struct hwrm_reject_fwd_resp_input, encap_resp_target_id))
#define BNXT_VF_MIN_RSS_CTX 1
#define BNXT_VF_MAX_RSS_CTX 1
#define BNXT_VF_MIN_L2_CTX 1
#define BNXT_VF_MAX_L2_CTX 4
int bnxt_get_vf_config(struct net_device *, int, struct ifla_vf_info *);
int bnxt_set_vf_mac(struct net_device *, int, u8 *);
int bnxt_set_vf_vlan(struct net_device *, int, u16, u8, __be16);
int bnxt_set_vf_bw(struct net_device *, int, int, int);
int bnxt_set_vf_link_state(struct net_device *, int, int);
int bnxt_set_vf_spoofchk(struct net_device *, int, bool);
bool bnxt_is_trusted_vf(struct bnxt *bp, struct bnxt_vf_info *vf);
int bnxt_set_vf_trust(struct net_device *dev, int vf_id, bool trust);
int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs);
int bnxt_cfg_hw_sriov(struct bnxt *bp, int *num_vfs, bool reset);
void __bnxt_sriov_disable(struct bnxt *bp);
void bnxt_hwrm_exec_fwd_req(struct bnxt *);
void bnxt_update_vf_mac(struct bnxt *);
int bnxt_approve_mac(struct bnxt *, const u8 *, bool);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,255 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_TC_H
#define BNXT_TC_H
#ifdef CONFIG_BNXT_FLOWER_OFFLOAD
#include <net/ip_tunnels.h>
/* Structs used for storing the filter/actions of the TC cmd.
*/
struct bnxt_tc_l2_key {
u8 dmac[ETH_ALEN];
u8 smac[ETH_ALEN];
__be16 inner_vlan_tpid;
__be16 inner_vlan_tci;
__be16 ether_type;
u8 num_vlans;
u8 dir;
#define BNXT_DIR_RX 1
#define BNXT_DIR_TX 0
};
struct bnxt_tc_l3_key {
union {
struct {
struct in_addr daddr;
struct in_addr saddr;
} ipv4;
struct {
struct in6_addr daddr;
struct in6_addr saddr;
} ipv6;
};
};
struct bnxt_tc_l4_key {
u8 ip_proto;
union {
struct {
__be16 sport;
__be16 dport;
} ports;
struct {
u8 type;
u8 code;
} icmp;
};
};
struct bnxt_tc_tunnel_key {
struct bnxt_tc_l2_key l2;
struct bnxt_tc_l3_key l3;
struct bnxt_tc_l4_key l4;
__be32 id;
};
#define bnxt_eth_addr_key_mask_invalid(eth_addr, eth_addr_mask) \
((is_wildcard(&(eth_addr)[0], ETH_ALEN) && \
is_wildcard(&(eth_addr)[ETH_ALEN / 2], ETH_ALEN)) || \
(is_wildcard(&(eth_addr_mask)[0], ETH_ALEN) && \
is_wildcard(&(eth_addr_mask)[ETH_ALEN / 2], ETH_ALEN)))
struct bnxt_tc_actions {
u32 flags;
#define BNXT_TC_ACTION_FLAG_FWD BIT(0)
#define BNXT_TC_ACTION_FLAG_FWD_VXLAN BIT(1)
#define BNXT_TC_ACTION_FLAG_PUSH_VLAN BIT(3)
#define BNXT_TC_ACTION_FLAG_POP_VLAN BIT(4)
#define BNXT_TC_ACTION_FLAG_DROP BIT(5)
#define BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP BIT(6)
#define BNXT_TC_ACTION_FLAG_TUNNEL_DECAP BIT(7)
#define BNXT_TC_ACTION_FLAG_L2_REWRITE BIT(8)
#define BNXT_TC_ACTION_FLAG_NAT_XLATE BIT(9)
u16 dst_fid;
struct net_device *dst_dev;
__be16 push_vlan_tpid;
__be16 push_vlan_tci;
/* tunnel encap */
struct ip_tunnel_key tun_encap_key;
#define PEDIT_OFFSET_SMAC_LAST_4_BYTES 0x8
__be16 l2_rewrite_dmac[3];
__be16 l2_rewrite_smac[3];
struct {
bool src_xlate; /* true => translate src,
* false => translate dst
* Mutually exclusive, i.e cannot set both
*/
bool l3_is_ipv4; /* false means L3 is ipv6 */
struct bnxt_tc_l3_key l3;
struct bnxt_tc_l4_key l4;
} nat;
};
struct bnxt_tc_flow {
u32 flags;
#define BNXT_TC_FLOW_FLAGS_ETH_ADDRS BIT(1)
#define BNXT_TC_FLOW_FLAGS_IPV4_ADDRS BIT(2)
#define BNXT_TC_FLOW_FLAGS_IPV6_ADDRS BIT(3)
#define BNXT_TC_FLOW_FLAGS_PORTS BIT(4)
#define BNXT_TC_FLOW_FLAGS_ICMP BIT(5)
#define BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS BIT(6)
#define BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS BIT(7)
#define BNXT_TC_FLOW_FLAGS_TUNL_IPV6_ADDRS BIT(8)
#define BNXT_TC_FLOW_FLAGS_TUNL_PORTS BIT(9)
#define BNXT_TC_FLOW_FLAGS_TUNL_ID BIT(10)
#define BNXT_TC_FLOW_FLAGS_TUNNEL (BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS | \
BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS | \
BNXT_TC_FLOW_FLAGS_TUNL_IPV6_ADDRS |\
BNXT_TC_FLOW_FLAGS_TUNL_PORTS |\
BNXT_TC_FLOW_FLAGS_TUNL_ID)
/* flow applicable to pkts ingressing on this fid */
u16 src_fid;
struct bnxt_tc_l2_key l2_key;
struct bnxt_tc_l2_key l2_mask;
struct bnxt_tc_l3_key l3_key;
struct bnxt_tc_l3_key l3_mask;
struct bnxt_tc_l4_key l4_key;
struct bnxt_tc_l4_key l4_mask;
struct ip_tunnel_key tun_key;
struct ip_tunnel_key tun_mask;
struct bnxt_tc_actions actions;
/* updated stats accounting for hw-counter wrap-around */
struct bnxt_tc_flow_stats stats;
/* previous snap-shot of stats */
struct bnxt_tc_flow_stats prev_stats;
unsigned long lastused; /* jiffies */
/* for calculating delta from prev_stats and
* updating prev_stats atomically.
*/
spinlock_t stats_lock;
};
/* Tunnel encap/decap hash table
* This table is used to maintain a list of flows that use
* the same tunnel encap/decap params (ip_daddrs, vni, udp_dport)
* and the FW returned handle.
* A separate table is maintained for encap and decap
*/
struct bnxt_tc_tunnel_node {
struct ip_tunnel_key key;
struct rhash_head node;
/* tunnel l2 info */
struct bnxt_tc_l2_key l2_info;
#define INVALID_TUNNEL_HANDLE cpu_to_le32(0xffffffff)
/* tunnel handle returned by FW */
__le32 tunnel_handle;
u32 refcount;
struct rcu_head rcu;
};
/* L2 hash table
* The same data-struct is used for L2-flow table and L2-tunnel table.
* The L2 part of a flow or tunnel is stored in a hash table.
* A flow that shares the same L2 key/mask with an
* already existing flow/tunnel must refer to it's flow handle or
* decap_filter_id respectively.
*/
struct bnxt_tc_l2_node {
/* hash key: first 16b of key */
#define BNXT_TC_L2_KEY_LEN 16
struct bnxt_tc_l2_key key;
struct rhash_head node;
/* a linked list of flows that share the same l2 key */
struct list_head common_l2_flows;
/* number of flows/tunnels sharing the l2 key */
u16 refcount;
struct rcu_head rcu;
};
struct bnxt_tc_flow_node {
/* hash key: provided by TC */
unsigned long cookie;
struct rhash_head node;
struct bnxt_tc_flow flow;
__le64 ext_flow_handle;
__le16 flow_handle;
__le32 flow_id;
/* L2 node in l2 hashtable that shares flow's l2 key */
struct bnxt_tc_l2_node *l2_node;
/* for the shared_flows list maintained in l2_node */
struct list_head l2_list_node;
/* tunnel encap related */
struct bnxt_tc_tunnel_node *encap_node;
/* tunnel decap related */
struct bnxt_tc_tunnel_node *decap_node;
/* L2 node in tunnel-l2 hashtable that shares flow's tunnel l2 key */
struct bnxt_tc_l2_node *decap_l2_node;
/* for the shared_flows list maintained in tunnel decap l2_node */
struct list_head decap_l2_list_node;
struct rcu_head rcu;
};
int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid,
struct flow_cls_offload *cls_flower);
int bnxt_init_tc(struct bnxt *bp);
void bnxt_shutdown_tc(struct bnxt *bp);
void bnxt_tc_flow_stats_work(struct bnxt *bp);
static inline bool bnxt_tc_flower_enabled(struct bnxt *bp)
{
return bp->tc_info && bp->tc_info->enabled;
}
#else /* CONFIG_BNXT_FLOWER_OFFLOAD */
static inline int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid,
struct flow_cls_offload *cls_flower)
{
return -EOPNOTSUPP;
}
static inline int bnxt_init_tc(struct bnxt *bp)
{
return 0;
}
static inline void bnxt_shutdown_tc(struct bnxt *bp)
{
}
static inline void bnxt_tc_flow_stats_work(struct bnxt *bp)
{
}
static inline bool bnxt_tc_flower_enabled(struct bnxt *bp)
{
return false;
}
#endif /* CONFIG_BNXT_FLOWER_OFFLOAD */
#endif /* BNXT_TC_H */
@@ -0,0 +1,628 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2016-2018 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <asm/byteorder.h>
#include <linux/bitmap.h>
#include <linux/auxiliary_bus.h>
#include <net/netdev_lock.h>
#include <linux/bnxt/hsi.h>
#include <linux/bnxt/ulp.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
static DEFINE_IDA(bnxt_aux_dev_ids);
struct bnxt_aux_device {
const char *name;
};
static void bnxt_auxdev_set_state(struct bnxt *bp, int idx, int state)
{
bp->auxdev_state[idx] = state;
}
static bool bnxt_auxdev_is_init(struct bnxt *bp, int idx)
{
return (bp->auxdev_state[idx] == BNXT_ADEV_STATE_INIT);
}
static bool bnxt_auxdev_is_active(struct bnxt *bp, int idx)
{
return (bp->auxdev_state[idx] == BNXT_ADEV_STATE_ADD);
}
static struct bnxt_aux_device bnxt_aux_devices[__BNXT_AUXDEV_MAX] = {{
.name = "rdma",
}, {
.name = "fwctl",
}};
static void bnxt_fill_msix_vecs(struct bnxt *bp, struct bnxt_msix_entry *ent)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
int num_msix, i;
if (!edev->ulp_tbl->msix_requested) {
netdev_warn(bp->dev, "Requested MSI-X vectors insufficient\n");
return;
}
num_msix = edev->ulp_tbl->msix_requested;
for (i = 0; i < num_msix; i++) {
ent[i].vector = bp->irq_tbl[i].vector;
ent[i].ring_idx = i;
if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
ent[i].db_offset = bp->db_offset;
else
ent[i].db_offset = i * 0x80;
}
}
int bnxt_get_ulp_msix_num(struct bnxt *bp)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
if (edev)
return edev->ulp_num_msix_vec;
return 0;
}
void bnxt_set_ulp_msix_num(struct bnxt *bp, int num)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
if (edev)
edev->ulp_num_msix_vec = num;
}
int bnxt_get_ulp_msix_num_in_use(struct bnxt *bp)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
if (bnxt_ulp_registered(edev))
return edev->ulp_num_msix_vec;
return 0;
}
int bnxt_get_ulp_stat_ctxs(struct bnxt *bp)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
if (edev)
return edev->ulp_num_ctxs;
return 0;
}
void bnxt_set_ulp_stat_ctxs(struct bnxt *bp, int num_ulp_ctx)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
if (edev)
edev->ulp_num_ctxs = num_ulp_ctx;
}
int bnxt_get_ulp_stat_ctxs_in_use(struct bnxt *bp)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
if (bnxt_ulp_registered(edev))
return edev->ulp_num_ctxs;
return 0;
}
void bnxt_set_dflt_ulp_stat_ctxs(struct bnxt *bp)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
if (edev) {
edev->ulp_num_ctxs = BNXT_MIN_ROCE_STAT_CTXS;
/* Reserve one additional stat_ctx for PF0 (except
* on 1-port NICs) as it also creates one stat_ctx
* for PF1 in case of RoCE bonding.
*/
if (BNXT_PF(bp) && !bp->pf.port_id &&
bp->port_count > 1)
edev->ulp_num_ctxs++;
/* Reserve one additional stat_ctx when the device is capable
* of supporting port mirroring on RDMA device.
*/
if (BNXT_MIRROR_ON_ROCE_CAP(bp))
edev->ulp_num_ctxs++;
}
}
int bnxt_register_dev(struct bnxt_en_dev *edev,
struct bnxt_ulp_ops *ulp_ops,
void *handle)
{
struct net_device *dev = edev->net;
struct bnxt *bp = netdev_priv(dev);
unsigned int max_stat_ctxs;
struct bnxt_ulp *ulp;
int rc = 0;
netdev_lock(dev);
mutex_lock(&edev->en_dev_lock);
if (!bp->irq_tbl) {
rc = -ENODEV;
goto exit;
}
max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp);
if (max_stat_ctxs <= BNXT_MIN_ROCE_STAT_CTXS ||
bp->cp_nr_rings == max_stat_ctxs) {
rc = -ENOMEM;
goto exit;
}
ulp = edev->ulp_tbl;
ulp->handle = handle;
rcu_assign_pointer(ulp->ulp_ops, ulp_ops);
if (test_bit(BNXT_STATE_OPEN, &bp->state)) {
rc = bnxt_hwrm_vnic_cfg(bp, &bp->vnic_info[BNXT_VNIC_DEFAULT]);
if (rc) {
netdev_err(dev, "Failed to configure dual VNIC mode\n");
RCU_INIT_POINTER(ulp->ulp_ops, NULL);
goto exit;
}
}
edev->ulp_tbl->msix_requested = bnxt_get_ulp_msix_num(bp);
bnxt_fill_msix_vecs(bp, edev->msix_entries);
exit:
mutex_unlock(&edev->en_dev_lock);
netdev_unlock(dev);
return rc;
}
EXPORT_SYMBOL(bnxt_register_dev);
void bnxt_unregister_dev(struct bnxt_en_dev *edev)
{
struct net_device *dev = edev->net;
struct bnxt *bp = netdev_priv(dev);
struct bnxt_ulp *ulp;
ulp = edev->ulp_tbl;
netdev_lock(dev);
mutex_lock(&edev->en_dev_lock);
edev->ulp_tbl->msix_requested = 0;
if (ulp->max_async_event_id)
bnxt_hwrm_func_drv_rgtr(bp, NULL, 0, true);
RCU_INIT_POINTER(ulp->ulp_ops, NULL);
synchronize_rcu();
ulp->max_async_event_id = 0;
ulp->async_events_bmap = NULL;
mutex_unlock(&edev->en_dev_lock);
netdev_unlock(dev);
return;
}
EXPORT_SYMBOL(bnxt_unregister_dev);
static int bnxt_set_dflt_ulp_msix(struct bnxt *bp)
{
int roce_msix = BNXT_MAX_ROCE_MSIX;
if (BNXT_VF(bp))
roce_msix = BNXT_MAX_ROCE_MSIX_VF;
else if (bp->port_partition_type)
roce_msix = BNXT_MAX_ROCE_MSIX_NPAR_PF;
/* NQ MSIX vectors should match the number of CPUs plus 1 more for
* the CREQ MSIX, up to the default.
*/
return min_t(int, roce_msix, num_online_cpus() + 1);
}
int bnxt_send_msg(struct bnxt_en_dev *edev,
struct bnxt_fw_msg *fw_msg)
{
struct net_device *dev = edev->net;
struct bnxt *bp = netdev_priv(dev);
struct output *resp;
struct input *req;
u32 resp_len;
int rc;
if (bp->fw_reset_state)
return -EBUSY;
rc = hwrm_req_init(bp, req, 0 /* don't care */);
if (rc)
return rc;
rc = hwrm_req_replace(bp, req, fw_msg->msg, fw_msg->msg_len);
if (rc)
goto drop_req;
hwrm_req_timeout(bp, req, fw_msg->timeout);
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
resp_len = le16_to_cpu(resp->resp_len);
if (resp_len) {
if (fw_msg->resp_max_len < resp_len)
resp_len = fw_msg->resp_max_len;
memcpy(fw_msg->resp, resp, resp_len);
}
drop_req:
hwrm_req_drop(bp, req);
return rc;
}
EXPORT_SYMBOL(bnxt_send_msg);
void bnxt_ulp_stop(struct bnxt *bp)
{
int i;
mutex_lock(&bp->auxdev_lock);
for (i = 0; i < __BNXT_AUXDEV_MAX; i++) {
struct bnxt_aux_priv *aux_priv;
struct auxiliary_device *adev;
struct bnxt_en_dev *edev;
if (!bnxt_auxdev_is_active(bp, i))
continue;
aux_priv = bp->aux_priv[i];
edev = bp->edev[i];
mutex_lock(&edev->en_dev_lock);
if (i == BNXT_AUXDEV_FWCTL) {
edev->flags |= BNXT_EN_FLAG_ULP_STOPPED;
mutex_unlock(&edev->en_dev_lock);
continue;
}
if (!bnxt_ulp_registered(edev) ||
(edev->flags & BNXT_EN_FLAG_ULP_STOPPED)) {
mutex_unlock(&edev->en_dev_lock);
continue;
}
edev->flags |= BNXT_EN_FLAG_ULP_STOPPED;
adev = &aux_priv->aux_dev;
if (adev->dev.driver) {
const struct auxiliary_driver *adrv;
pm_message_t pm = {};
adrv = to_auxiliary_drv(adev->dev.driver);
edev->en_state = bp->state;
adrv->suspend(adev, pm);
}
mutex_unlock(&edev->en_dev_lock);
}
mutex_unlock(&bp->auxdev_lock);
}
void bnxt_ulp_start(struct bnxt *bp)
{
int i;
mutex_lock(&bp->auxdev_lock);
for (i = 0; i < __BNXT_AUXDEV_MAX; i++) {
struct bnxt_aux_priv *aux_priv;
struct auxiliary_device *adev;
struct bnxt_en_dev *edev;
if (!bnxt_auxdev_is_active(bp, i))
continue;
aux_priv = bp->aux_priv[i];
edev = bp->edev[i];
mutex_lock(&edev->en_dev_lock);
if (i == BNXT_AUXDEV_FWCTL || !bnxt_ulp_registered(edev) ||
!(edev->flags & BNXT_EN_FLAG_ULP_STOPPED)) {
goto clear_flag_continue;
}
if (edev->ulp_tbl->msix_requested)
bnxt_fill_msix_vecs(bp, edev->msix_entries);
adev = &aux_priv->aux_dev;
if (adev->dev.driver) {
const struct auxiliary_driver *adrv;
adrv = to_auxiliary_drv(adev->dev.driver);
edev->en_state = bp->state;
adrv->resume(adev);
}
clear_flag_continue:
edev->flags &= ~BNXT_EN_FLAG_ULP_STOPPED;
mutex_unlock(&edev->en_dev_lock);
}
mutex_unlock(&bp->auxdev_lock);
}
void bnxt_ulp_irq_stop(struct bnxt *bp)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
struct bnxt_ulp_ops *ops;
bool reset = false;
if (!edev)
return;
if (bnxt_ulp_registered(edev)) {
struct bnxt_ulp *ulp = edev->ulp_tbl;
if (!ulp->msix_requested)
return;
ops = netdev_lock_dereference(ulp->ulp_ops, bp->dev);
if (!ops || !ops->ulp_irq_stop)
return;
if (test_bit(BNXT_STATE_FW_RESET_DET, &bp->state))
reset = true;
ops->ulp_irq_stop(ulp->handle, reset);
}
}
void bnxt_ulp_irq_restart(struct bnxt *bp, int err)
{
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
struct bnxt_ulp_ops *ops;
if (!edev)
return;
if (bnxt_ulp_registered(edev)) {
struct bnxt_ulp *ulp = edev->ulp_tbl;
struct bnxt_msix_entry *ent = NULL;
if (!ulp->msix_requested)
return;
ops = netdev_lock_dereference(ulp->ulp_ops, bp->dev);
if (!ops || !ops->ulp_irq_restart)
return;
if (!err) {
ent = kzalloc_objs(*ent, ulp->msix_requested);
if (!ent)
return;
bnxt_fill_msix_vecs(bp, ent);
}
ops->ulp_irq_restart(ulp->handle, ent);
kfree(ent);
}
}
void bnxt_ulp_async_events(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl)
{
u16 event_id = le16_to_cpu(cmpl->event_id);
struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA];
struct bnxt_ulp_ops *ops;
struct bnxt_ulp *ulp;
if (!bnxt_ulp_registered(edev))
return;
ulp = edev->ulp_tbl;
rcu_read_lock();
ops = rcu_dereference(ulp->ulp_ops);
if (!ops || !ops->ulp_async_notifier)
goto exit_unlock_rcu;
if (!ulp->async_events_bmap || event_id > ulp->max_async_event_id)
goto exit_unlock_rcu;
/* Read max_async_event_id first before testing the bitmap. */
smp_rmb();
if (test_bit(event_id, ulp->async_events_bmap))
ops->ulp_async_notifier(ulp->handle, cmpl);
exit_unlock_rcu:
rcu_read_unlock();
}
void bnxt_register_async_events(struct bnxt_en_dev *edev,
unsigned long *events_bmap, u16 max_id)
{
struct net_device *dev = edev->net;
struct bnxt *bp = netdev_priv(dev);
struct bnxt_ulp *ulp;
ulp = edev->ulp_tbl;
ulp->async_events_bmap = events_bmap;
/* Make sure bnxt_ulp_async_events() sees this order */
smp_wmb();
ulp->max_async_event_id = max_id;
bnxt_hwrm_func_drv_rgtr(bp, events_bmap, max_id + 1, true);
}
EXPORT_SYMBOL(bnxt_register_async_events);
void bnxt_aux_devices_uninit(struct bnxt *bp)
{
struct bnxt_aux_priv *aux_priv;
struct auxiliary_device *adev;
int idx;
mutex_lock(&bp->auxdev_lock);
for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) {
if (bnxt_auxdev_is_init(bp, idx)) {
aux_priv = bp->aux_priv[idx];
adev = &aux_priv->aux_dev;
auxiliary_device_uninit(adev);
}
}
mutex_unlock(&bp->auxdev_lock);
}
static void bnxt_aux_dev_release(struct device *dev)
{
struct bnxt_aux_priv *aux_priv =
container_of(dev, struct bnxt_aux_priv, aux_dev.dev);
struct bnxt *bp = netdev_priv(aux_priv->edev->net);
kfree(aux_priv->edev->ulp_tbl);
bp->edev[aux_priv->id] = NULL;
kfree(aux_priv->edev);
bp->aux_priv[aux_priv->id] = NULL;
kfree(aux_priv);
}
void bnxt_aux_devices_del(struct bnxt *bp)
{
int idx;
mutex_lock(&bp->auxdev_lock);
for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) {
if (bnxt_auxdev_is_active(bp, idx)) {
auxiliary_device_delete(&bp->aux_priv[idx]->aux_dev);
bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_INIT);
}
}
mutex_unlock(&bp->auxdev_lock);
}
static void bnxt_set_edev_info(struct bnxt_en_dev *edev, struct bnxt *bp)
{
edev->net = bp->dev;
edev->pdev = bp->pdev;
edev->l2_db_size = bp->db_size;
edev->l2_db_size_nc = bp->db_size;
edev->l2_db_offset = bp->db_offset;
mutex_init(&edev->en_dev_lock);
if (bp->flags & BNXT_FLAG_ROCEV1_CAP)
edev->flags |= BNXT_EN_FLAG_ROCEV1_CAP;
if (bp->flags & BNXT_FLAG_ROCEV2_CAP)
edev->flags |= BNXT_EN_FLAG_ROCEV2_CAP;
if (bp->flags & BNXT_FLAG_VF)
edev->flags |= BNXT_EN_FLAG_VF;
if (BNXT_ROCE_VF_RESC_CAP(bp))
edev->flags |= BNXT_EN_FLAG_ROCE_VF_RES_MGMT;
if (BNXT_SW_RES_LMT(bp))
edev->flags |= BNXT_EN_FLAG_SW_RES_LMT;
edev->chip_num = bp->chip_num;
edev->hw_ring_stats_size = bp->hw_ring_stats_size;
edev->pf_port_id = bp->pf.port_id;
edev->en_state = bp->state;
edev->bar0 = bp->bar0;
}
void bnxt_aux_devices_add(struct bnxt *bp)
{
struct auxiliary_device *aux_dev;
int rc, idx;
mutex_lock(&bp->auxdev_lock);
for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) {
if (bnxt_auxdev_is_init(bp, idx)) {
aux_dev = &bp->aux_priv[idx]->aux_dev;
rc = auxiliary_device_add(aux_dev);
if (rc) {
netdev_warn(bp->dev, "Failed to add auxiliary device for auxdev type %d\n",
idx);
auxiliary_device_uninit(aux_dev);
if (idx == BNXT_AUXDEV_RDMA)
bp->flags &= ~BNXT_FLAG_ROCE_CAP;
continue;
}
bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_ADD);
}
}
mutex_unlock(&bp->auxdev_lock);
}
void bnxt_aux_devices_init(struct bnxt *bp)
{
struct auxiliary_device *aux_dev;
struct bnxt_aux_priv *aux_priv;
struct bnxt_en_dev *edev;
struct bnxt_ulp *ulp;
int rc, idx;
mutex_lock(&bp->auxdev_lock);
for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) {
bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_NONE);
if (idx == BNXT_AUXDEV_RDMA &&
!(bp->flags & BNXT_FLAG_ROCE_CAP))
continue;
aux_priv = kzalloc_obj(*aux_priv);
if (!aux_priv)
goto next_auxdev;
edev = kzalloc_obj(*edev);
if (!edev)
goto aux_priv_free;
aux_priv->edev = edev;
bnxt_set_edev_info(edev, bp);
ulp = kzalloc_obj(*ulp);
if (!ulp)
goto edev_free;
edev->ulp_tbl = ulp;
aux_priv->id = idx;
aux_dev = &aux_priv->aux_dev;
aux_dev->id = bp->auxdev_id;
aux_dev->name = bnxt_aux_devices[idx].name;
aux_dev->dev.parent = &bp->pdev->dev;
aux_dev->dev.release = bnxt_aux_dev_release;
rc = auxiliary_device_init(aux_dev);
if (rc)
goto ulp_free;
bp->aux_priv[idx] = aux_priv;
/* From this point, all cleanup will happen via the .release
* callback & any error unwinding will need to include a call
* to auxiliary_device_uninit.
*/
bp->edev[idx] = edev;
if (idx == BNXT_AUXDEV_RDMA)
bp->ulp_num_msix_want = bnxt_set_dflt_ulp_msix(bp);
bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_INIT);
continue;
ulp_free:
kfree(ulp);
edev_free:
kfree(edev);
aux_priv_free:
kfree(aux_priv);
next_auxdev:
if (idx == BNXT_AUXDEV_RDMA)
bp->flags &= ~BNXT_FLAG_ROCE_CAP;
}
mutex_unlock(&bp->auxdev_lock);
}
int bnxt_auxdev_id_alloc(struct bnxt *bp)
{
bp->auxdev_id = ida_alloc(&bnxt_aux_dev_ids, GFP_KERNEL);
if (bp->auxdev_id < 0)
return bp->auxdev_id;
return 0;
}
void bnxt_auxdev_id_free(struct bnxt *bp, int id)
{
if (bp->auxdev_id >= 0)
ida_free(&bnxt_aux_dev_ids, id);
}
@@ -0,0 +1,600 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2016-2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/jhash.h>
#include <net/pkt_cls.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_vfr.h"
#include "bnxt_devlink.h"
#include "bnxt_tc.h"
#ifdef CONFIG_BNXT_SRIOV
#define CFA_HANDLE_INVALID 0xffff
#define VF_IDX_INVALID 0xffff
static int hwrm_cfa_vfr_alloc(struct bnxt *bp, u16 vf_idx,
u16 *tx_cfa_action, u16 *rx_cfa_code)
{
struct hwrm_cfa_vfr_alloc_output *resp;
struct hwrm_cfa_vfr_alloc_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_CFA_VFR_ALLOC);
if (!rc) {
req->vf_id = cpu_to_le16(vf_idx);
sprintf(req->vfr_name, "vfr%d", vf_idx);
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
if (!rc) {
*tx_cfa_action = le16_to_cpu(resp->tx_cfa_action);
*rx_cfa_code = le16_to_cpu(resp->rx_cfa_code);
netdev_dbg(bp->dev, "tx_cfa_action=0x%x, rx_cfa_code=0x%x",
*tx_cfa_action, *rx_cfa_code);
}
hwrm_req_drop(bp, req);
}
if (rc)
netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc);
return rc;
}
static int hwrm_cfa_vfr_free(struct bnxt *bp, u16 vf_idx)
{
struct hwrm_cfa_vfr_free_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_CFA_VFR_FREE);
if (!rc) {
sprintf(req->vfr_name, "vfr%d", vf_idx);
rc = hwrm_req_send(bp, req);
}
if (rc)
netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc);
return rc;
}
static int bnxt_hwrm_vfr_qcfg(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
u16 *max_mtu)
{
struct hwrm_func_qcfg_output *resp;
struct hwrm_func_qcfg_input *req;
u16 mtu;
int rc;
rc = hwrm_req_init(bp, req, HWRM_FUNC_QCFG);
if (rc)
return rc;
req->fid = cpu_to_le16(bp->pf.vf[vf_rep->vf_idx].fw_fid);
resp = hwrm_req_hold(bp, req);
rc = hwrm_req_send(bp, req);
if (!rc) {
mtu = le16_to_cpu(resp->max_mtu_configured);
if (!mtu)
*max_mtu = BNXT_MAX_MTU;
else
*max_mtu = mtu;
}
hwrm_req_drop(bp, req);
return rc;
}
static int bnxt_vf_rep_open(struct net_device *dev)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
struct bnxt *bp = vf_rep->bp;
/* Enable link and TX only if the parent PF is open. */
if (netif_running(bp->dev)) {
netif_carrier_on(dev);
netif_tx_start_all_queues(dev);
}
return 0;
}
static int bnxt_vf_rep_close(struct net_device *dev)
{
netif_carrier_off(dev);
netif_tx_disable(dev);
return 0;
}
static netdev_tx_t bnxt_vf_rep_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
int rc, len = skb->len;
skb_dst_drop(skb);
dst_hold((struct dst_entry *)vf_rep->dst);
skb_dst_set(skb, (struct dst_entry *)vf_rep->dst);
skb->dev = vf_rep->dst->u.port_info.lower_dev;
rc = dev_queue_xmit(skb);
if (!rc) {
vf_rep->tx_stats.packets++;
vf_rep->tx_stats.bytes += len;
}
return rc;
}
static void
bnxt_vf_rep_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
stats->rx_packets = vf_rep->rx_stats.packets;
stats->rx_bytes = vf_rep->rx_stats.bytes;
stats->tx_packets = vf_rep->tx_stats.packets;
stats->tx_bytes = vf_rep->tx_stats.bytes;
}
static int bnxt_vf_rep_setup_tc_block_cb(enum tc_setup_type type,
void *type_data,
void *cb_priv)
{
struct bnxt_vf_rep *vf_rep = cb_priv;
struct bnxt *bp = vf_rep->bp;
int vf_fid = bp->pf.vf[vf_rep->vf_idx].fw_fid;
if (!bnxt_tc_flower_enabled(vf_rep->bp) ||
!tc_cls_can_offload_and_chain0(bp->dev, type_data))
return -EOPNOTSUPP;
switch (type) {
case TC_SETUP_CLSFLOWER:
return bnxt_tc_setup_flower(bp, vf_fid, type_data);
default:
return -EOPNOTSUPP;
}
}
static LIST_HEAD(bnxt_vf_block_cb_list);
static int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
switch (type) {
case TC_SETUP_BLOCK:
return flow_block_cb_setup_simple(type_data,
&bnxt_vf_block_cb_list,
bnxt_vf_rep_setup_tc_block_cb,
vf_rep, vf_rep, true);
default:
return -EOPNOTSUPP;
}
}
struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code)
{
u16 vf_idx;
if (cfa_code && bp->cfa_code_map && BNXT_PF(bp)) {
vf_idx = bp->cfa_code_map[cfa_code];
if (vf_idx != VF_IDX_INVALID)
return bp->vf_reps[vf_idx]->dev;
}
return NULL;
}
void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(skb->dev);
vf_rep->rx_stats.bytes += skb->len;
vf_rep->rx_stats.packets++;
netif_receive_skb(skb);
}
static int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf,
size_t len)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
struct pci_dev *pf_pdev = vf_rep->bp->pdev;
int rc;
rc = snprintf(buf, len, "pf%dvf%d", PCI_FUNC(pf_pdev->devfn),
vf_rep->vf_idx);
if (rc >= len)
return -EOPNOTSUPP;
return 0;
}
static void bnxt_vf_rep_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
}
static int bnxt_vf_rep_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
/* as only PORT_PARENT_ID is supported currently use common code
* between PF and VF-rep for now.
*/
return bnxt_get_port_parent_id(vf_rep->bp->dev, ppid);
}
static const struct ethtool_ops bnxt_vf_rep_ethtool_ops = {
.get_drvinfo = bnxt_vf_rep_get_drvinfo
};
static const struct net_device_ops bnxt_vf_rep_netdev_ops = {
.ndo_open = bnxt_vf_rep_open,
.ndo_stop = bnxt_vf_rep_close,
.ndo_start_xmit = bnxt_vf_rep_xmit,
.ndo_get_stats64 = bnxt_vf_rep_get_stats64,
.ndo_setup_tc = bnxt_vf_rep_setup_tc,
.ndo_get_port_parent_id = bnxt_vf_rep_get_port_parent_id,
.ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name
};
bool bnxt_dev_is_vf_rep(struct net_device *dev)
{
return dev->netdev_ops == &bnxt_vf_rep_netdev_ops;
}
/* Called when the parent PF interface is closed:
* As the mode transition from SWITCHDEV to LEGACY
* happens under the netdev instance lock this routine is safe
*/
void bnxt_vf_reps_close(struct bnxt *bp)
{
struct bnxt_vf_rep *vf_rep;
u16 num_vfs, i;
if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
return;
num_vfs = pci_num_vf(bp->pdev);
for (i = 0; i < num_vfs; i++) {
vf_rep = bp->vf_reps[i];
if (netif_running(vf_rep->dev))
bnxt_vf_rep_close(vf_rep->dev);
}
}
/* Called when the parent PF interface is opened (re-opened):
* As the mode transition from SWITCHDEV to LEGACY
* happen under the netdev instance lock this routine is safe
*/
void bnxt_vf_reps_open(struct bnxt *bp)
{
int i;
if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
return;
for (i = 0; i < pci_num_vf(bp->pdev); i++) {
/* Open the VF-Rep only if it is allocated in the FW */
if (bp->vf_reps[i]->tx_cfa_action != CFA_HANDLE_INVALID)
bnxt_vf_rep_open(bp->vf_reps[i]->dev);
}
}
static void __bnxt_free_one_vf_rep(struct bnxt *bp, struct bnxt_vf_rep *vf_rep)
{
if (!vf_rep)
return;
if (vf_rep->dst) {
dst_release((struct dst_entry *)vf_rep->dst);
vf_rep->dst = NULL;
}
if (vf_rep->tx_cfa_action != CFA_HANDLE_INVALID) {
hwrm_cfa_vfr_free(bp, vf_rep->vf_idx);
vf_rep->tx_cfa_action = CFA_HANDLE_INVALID;
}
}
static void __bnxt_vf_reps_destroy(struct bnxt *bp)
{
u16 num_vfs = pci_num_vf(bp->pdev);
struct bnxt_vf_rep *vf_rep;
int i;
for (i = 0; i < num_vfs; i++) {
vf_rep = bp->vf_reps[i];
if (vf_rep) {
__bnxt_free_one_vf_rep(bp, vf_rep);
if (vf_rep->dev) {
/* if register_netdev failed, then netdev_ops
* would have been set to NULL
*/
if (vf_rep->dev->netdev_ops)
unregister_netdev(vf_rep->dev);
free_netdev(vf_rep->dev);
}
}
}
kfree(bp->vf_reps);
bp->vf_reps = NULL;
}
void bnxt_vf_reps_destroy(struct bnxt *bp)
{
bool closed = false;
if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
return;
if (!bp->vf_reps)
return;
/* Ensure that parent PF's and VF-reps' RX/TX has been quiesced
* before proceeding with VF-rep cleanup.
*/
netdev_lock(bp->dev);
if (netif_running(bp->dev)) {
bnxt_close_nic(bp, false, false);
closed = true;
}
/* un-publish cfa_code_map so that RX path can't see it anymore */
kfree(bp->cfa_code_map);
bp->cfa_code_map = NULL;
if (closed) {
/* Temporarily set legacy mode to avoid re-opening
* representors and restore switchdev mode after that.
*/
bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
bnxt_open_nic(bp, false, false);
bp->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
}
netdev_unlock(bp->dev);
/* Need to call vf_reps_destroy() outside of netdev instance lock
* as unregister_netdev takes it
*/
__bnxt_vf_reps_destroy(bp);
}
/* Free the VF-Reps in firmware, during firmware hot-reset processing.
* Note that the VF-Rep netdevs are still active (not unregistered) during
* this process. As the mode transition from SWITCHDEV to LEGACY happens
* under the netdev instance lock this routine is safe.
*/
void bnxt_vf_reps_free(struct bnxt *bp)
{
u16 num_vfs = pci_num_vf(bp->pdev);
int i;
if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
return;
for (i = 0; i < num_vfs; i++)
__bnxt_free_one_vf_rep(bp, bp->vf_reps[i]);
}
static int bnxt_alloc_vf_rep(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
u16 *cfa_code_map)
{
/* get cfa handles from FW */
if (hwrm_cfa_vfr_alloc(bp, vf_rep->vf_idx, &vf_rep->tx_cfa_action,
&vf_rep->rx_cfa_code))
return -ENOLINK;
cfa_code_map[vf_rep->rx_cfa_code] = vf_rep->vf_idx;
vf_rep->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, GFP_KERNEL);
if (!vf_rep->dst)
return -ENOMEM;
/* only cfa_action is needed to mux a packet while TXing */
vf_rep->dst->u.port_info.port_id = vf_rep->tx_cfa_action;
vf_rep->dst->u.port_info.lower_dev = bp->dev;
return 0;
}
/* Allocate the VF-Reps in firmware, during firmware hot-reset processing.
* Note that the VF-Rep netdevs are still active (not unregistered) during
* this process. As the mode transition from SWITCHDEV to LEGACY happens
* under the netdev instance lock this routine is safe.
*/
int bnxt_vf_reps_alloc(struct bnxt *bp)
{
u16 *cfa_code_map = bp->cfa_code_map, num_vfs = pci_num_vf(bp->pdev);
struct bnxt_vf_rep *vf_rep;
int rc, i;
if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
return 0;
if (!cfa_code_map)
return -EINVAL;
for (i = 0; i < MAX_CFA_CODE; i++)
cfa_code_map[i] = VF_IDX_INVALID;
for (i = 0; i < num_vfs; i++) {
vf_rep = bp->vf_reps[i];
vf_rep->vf_idx = i;
rc = bnxt_alloc_vf_rep(bp, vf_rep, cfa_code_map);
if (rc)
goto err;
}
return 0;
err:
netdev_info(bp->dev, "%s error=%d\n", __func__, rc);
bnxt_vf_reps_free(bp);
return rc;
}
/* Use the OUI of the PF's perm addr and report the same mac addr
* for the same VF-rep each time
*/
static void bnxt_vf_rep_eth_addr_gen(u8 *src_mac, u16 vf_idx, u8 *mac)
{
u32 addr;
ether_addr_copy(mac, src_mac);
addr = jhash(src_mac, ETH_ALEN, 0) + vf_idx;
mac[3] = (u8)(addr & 0xFF);
mac[4] = (u8)((addr >> 8) & 0xFF);
mac[5] = (u8)((addr >> 16) & 0xFF);
}
static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
struct net_device *dev)
{
struct net_device *pf_dev = bp->dev;
u16 max_mtu;
SET_NETDEV_DEV(dev, &bp->pdev->dev);
dev->netdev_ops = &bnxt_vf_rep_netdev_ops;
dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops;
/* Just inherit all the featues of the parent PF as the VF-R
* uses the RX/TX rings of the parent PF
*/
dev->hw_features = pf_dev->hw_features;
dev->gso_partial_features = pf_dev->gso_partial_features;
dev->vlan_features = pf_dev->vlan_features;
dev->hw_enc_features = pf_dev->hw_enc_features;
dev->features |= pf_dev->features;
bnxt_vf_rep_eth_addr_gen(bp->pf.mac_addr, vf_rep->vf_idx,
dev->perm_addr);
eth_hw_addr_set(dev, dev->perm_addr);
/* Set VF-Rep's max-mtu to the corresponding VF's max-mtu */
if (!bnxt_hwrm_vfr_qcfg(bp, vf_rep, &max_mtu))
dev->max_mtu = max_mtu;
dev->min_mtu = ETH_ZLEN;
}
int bnxt_vf_reps_create(struct bnxt *bp)
{
u16 *cfa_code_map = NULL, num_vfs = pci_num_vf(bp->pdev);
struct bnxt_vf_rep *vf_rep;
struct net_device *dev;
int rc, i;
if (!(bp->flags & BNXT_FLAG_DSN_VALID))
return -ENODEV;
bp->vf_reps = kzalloc_objs(vf_rep, num_vfs);
if (!bp->vf_reps)
return -ENOMEM;
/* storage for cfa_code to vf-idx mapping */
cfa_code_map = kmalloc_array(MAX_CFA_CODE, sizeof(*bp->cfa_code_map),
GFP_KERNEL);
if (!cfa_code_map) {
rc = -ENOMEM;
goto err;
}
for (i = 0; i < MAX_CFA_CODE; i++)
cfa_code_map[i] = VF_IDX_INVALID;
for (i = 0; i < num_vfs; i++) {
dev = alloc_etherdev(sizeof(*vf_rep));
if (!dev) {
rc = -ENOMEM;
goto err;
}
vf_rep = netdev_priv(dev);
bp->vf_reps[i] = vf_rep;
vf_rep->dev = dev;
vf_rep->bp = bp;
vf_rep->vf_idx = i;
vf_rep->tx_cfa_action = CFA_HANDLE_INVALID;
rc = bnxt_alloc_vf_rep(bp, vf_rep, cfa_code_map);
if (rc)
goto err;
bnxt_vf_rep_netdev_init(bp, vf_rep, dev);
rc = register_netdev(dev);
if (rc) {
/* no need for unregister_netdev in cleanup */
dev->netdev_ops = NULL;
goto err;
}
}
/* publish cfa_code_map only after all VF-reps have been initialized */
bp->cfa_code_map = cfa_code_map;
netif_keep_dst(bp->dev);
return 0;
err:
netdev_info(bp->dev, "%s error=%d\n", __func__, rc);
kfree(cfa_code_map);
__bnxt_vf_reps_destroy(bp);
return rc;
}
/* Devlink related routines */
int bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode)
{
struct bnxt *bp = bnxt_get_bp_from_dl(devlink);
*mode = bp->eswitch_mode;
return 0;
}
int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = bnxt_get_bp_from_dl(devlink);
int ret = 0;
if (bp->eswitch_mode == mode) {
netdev_info(bp->dev, "already in %s eswitch mode\n",
mode == DEVLINK_ESWITCH_MODE_LEGACY ?
"legacy" : "switchdev");
return -EINVAL;
}
switch (mode) {
case DEVLINK_ESWITCH_MODE_LEGACY:
bnxt_vf_reps_destroy(bp);
break;
case DEVLINK_ESWITCH_MODE_SWITCHDEV:
if (bp->hwrm_spec_code < 0x10803) {
netdev_warn(bp->dev, "FW does not support SRIOV E-Switch SWITCHDEV mode\n");
return -ENOTSUPP;
}
/* Create representors for existing VFs */
if (pci_num_vf(bp->pdev) > 0)
ret = bnxt_vf_reps_create(bp);
break;
default:
return -EINVAL;
}
if (!ret)
bp->eswitch_mode = mode;
return ret;
}
#endif
@@ -0,0 +1,83 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2016-2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_VFR_H
#define BNXT_VFR_H
#ifdef CONFIG_BNXT_SRIOV
#define MAX_CFA_CODE 65536
int bnxt_vf_reps_create(struct bnxt *bp);
void bnxt_vf_reps_destroy(struct bnxt *bp);
void bnxt_vf_reps_close(struct bnxt *bp);
void bnxt_vf_reps_open(struct bnxt *bp);
void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb);
struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code);
int bnxt_vf_reps_alloc(struct bnxt *bp);
void bnxt_vf_reps_free(struct bnxt *bp);
static inline u16 bnxt_vf_rep_get_fid(struct net_device *dev)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
struct bnxt *bp = vf_rep->bp;
return bp->pf.vf[vf_rep->vf_idx].fw_fid;
}
bool bnxt_dev_is_vf_rep(struct net_device *dev);
int bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode);
int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode,
struct netlink_ext_ack *extack);
#else
static inline int bnxt_vf_reps_create(struct bnxt *bp)
{
return 0;
}
static inline void bnxt_vf_reps_close(struct bnxt *bp)
{
}
static inline void bnxt_vf_reps_open(struct bnxt *bp)
{
}
static inline void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb)
{
}
static inline struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code)
{
return NULL;
}
static inline u16 bnxt_vf_rep_get_fid(struct net_device *dev)
{
return 0;
}
static inline bool bnxt_dev_is_vf_rep(struct net_device *dev)
{
return false;
}
static inline int bnxt_vf_reps_alloc(struct bnxt *bp)
{
return 0;
}
static inline void bnxt_vf_reps_free(struct bnxt *bp)
{
}
#endif /* CONFIG_BNXT_SRIOV */
#endif /* BNXT_VFR_H */
@@ -0,0 +1,530 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2016-2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/filter.h>
#include <net/netdev_lock.h>
#include <net/page_pool/helpers.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_xdp.h"
DEFINE_STATIC_KEY_FALSE(bnxt_xdp_locking_key);
struct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
dma_addr_t mapping, u32 len,
struct xdp_buff *xdp)
{
struct skb_shared_info *sinfo;
struct bnxt_sw_tx_bd *tx_buf;
struct tx_bd *txbd;
int num_frags = 0;
u32 flags;
u16 prod;
int i;
if (xdp && xdp_buff_has_frags(xdp)) {
sinfo = xdp_get_shared_info_from_buff(xdp);
num_frags = sinfo->nr_frags;
}
/* fill up the first buffer */
prod = txr->tx_prod;
tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
tx_buf->nr_frags = num_frags;
if (xdp)
tx_buf->page = virt_to_head_page(xdp->data);
txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
flags = (len << TX_BD_LEN_SHIFT) | TX_BD_CNT(num_frags + 1) |
bnxt_lhint_arr[len >> 9];
txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
txbd->tx_bd_opaque = SET_TX_OPAQUE(bp, txr, prod, 1 + num_frags);
txbd->tx_bd_haddr = cpu_to_le64(mapping);
/* now let us fill up the frags into the next buffers */
for (i = 0; i < num_frags ; i++) {
skb_frag_t *frag = &sinfo->frags[i];
struct bnxt_sw_tx_bd *frag_tx_buf;
dma_addr_t frag_mapping;
int frag_len;
prod = NEXT_TX(prod);
WRITE_ONCE(txr->tx_prod, prod);
/* first fill up the first buffer */
frag_tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
frag_tx_buf->page = skb_frag_page(frag);
txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
frag_len = skb_frag_size(frag);
flags = frag_len << TX_BD_LEN_SHIFT;
txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
frag_mapping = page_pool_get_dma_addr(skb_frag_page(frag)) +
skb_frag_off(frag);
txbd->tx_bd_haddr = cpu_to_le64(frag_mapping);
len = frag_len;
}
flags &= ~TX_BD_LEN;
txbd->tx_bd_len_flags_type = cpu_to_le32(((len) << TX_BD_LEN_SHIFT) | flags |
TX_BD_FLAGS_PACKET_END);
/* Sync TX BD */
wmb();
prod = NEXT_TX(prod);
WRITE_ONCE(txr->tx_prod, prod);
return tx_buf;
}
static void __bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
dma_addr_t mapping, u32 len, u16 rx_prod,
struct xdp_buff *xdp)
{
struct bnxt_sw_tx_bd *tx_buf;
tx_buf = bnxt_xmit_bd(bp, txr, mapping, len, xdp);
tx_buf->rx_prod = rx_prod;
tx_buf->action = XDP_TX;
}
static void __bnxt_xmit_xdp_redirect(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
dma_addr_t mapping, u32 len,
struct xdp_frame *xdpf)
{
struct bnxt_sw_tx_bd *tx_buf;
tx_buf = bnxt_xmit_bd(bp, txr, mapping, len, NULL);
tx_buf->action = XDP_REDIRECT;
tx_buf->xdpf = xdpf;
dma_unmap_addr_set(tx_buf, mapping, mapping);
dma_unmap_len_set(tx_buf, len, len);
}
void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int budget)
{
struct bnxt_tx_ring_info *txr = bnapi->tx_ring[0];
struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
u16 tx_hw_cons = txr->tx_hw_cons;
bool rx_doorbell_needed = false;
struct bnxt_sw_tx_bd *tx_buf;
u16 tx_cons = txr->tx_cons;
u16 last_tx_cons = tx_cons;
int j, frags;
if (!budget)
return;
while (RING_TX(bp, tx_cons) != tx_hw_cons) {
tx_buf = &txr->tx_buf_ring[RING_TX(bp, tx_cons)];
if (tx_buf->action == XDP_REDIRECT) {
struct pci_dev *pdev = bp->pdev;
dma_unmap_single(&pdev->dev,
dma_unmap_addr(tx_buf, mapping),
dma_unmap_len(tx_buf, len),
DMA_TO_DEVICE);
xdp_return_frame(tx_buf->xdpf);
tx_buf->action = 0;
tx_buf->xdpf = NULL;
} else if (tx_buf->action == XDP_TX) {
tx_buf->action = 0;
rx_doorbell_needed = true;
last_tx_cons = tx_cons;
frags = tx_buf->nr_frags;
for (j = 0; j < frags; j++) {
tx_cons = NEXT_TX(tx_cons);
tx_buf = &txr->tx_buf_ring[RING_TX(bp, tx_cons)];
page_pool_recycle_direct(rxr->page_pool, tx_buf->page);
}
} else {
bnxt_sched_reset_txr(bp, txr, tx_cons);
return;
}
tx_cons = NEXT_TX(tx_cons);
}
bnapi->events &= ~BNXT_TX_CMP_EVENT;
WRITE_ONCE(txr->tx_cons, tx_cons);
if (rx_doorbell_needed) {
tx_buf = &txr->tx_buf_ring[RING_TX(bp, last_tx_cons)];
bnxt_db_write(bp, &rxr->rx_db, tx_buf->rx_prod);
}
}
bool bnxt_xdp_attached(struct bnxt *bp, struct bnxt_rx_ring_info *rxr)
{
struct bpf_prog *xdp_prog = READ_ONCE(rxr->xdp_prog);
return !!xdp_prog;
}
void bnxt_xdp_buff_init(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
u16 cons, u8 *data_ptr, unsigned int len,
struct xdp_buff *xdp)
{
u32 buflen = rxr->rx_page_size;
struct bnxt_sw_rx_bd *rx_buf;
struct pci_dev *pdev;
dma_addr_t mapping;
u32 offset;
pdev = bp->pdev;
rx_buf = &rxr->rx_buf_ring[cons];
offset = bp->rx_offset;
mapping = rx_buf->mapping - bp->rx_dma_offset;
dma_sync_single_for_cpu(&pdev->dev, mapping + offset, len, bp->rx_dir);
xdp_init_buff(xdp, buflen, &rxr->xdp_rxq);
xdp_prepare_buff(xdp, data_ptr - offset, offset, len, true);
}
void bnxt_xdp_buff_frags_free(struct bnxt_rx_ring_info *rxr,
struct xdp_buff *xdp)
{
struct skb_shared_info *shinfo;
int i;
if (!xdp || !xdp_buff_has_frags(xdp))
return;
shinfo = xdp_get_shared_info_from_buff(xdp);
for (i = 0; i < shinfo->nr_frags; i++) {
struct page *page = skb_frag_page(&shinfo->frags[i]);
page_pool_recycle_direct(rxr->page_pool, page);
}
shinfo->nr_frags = 0;
}
/* returns the following:
* true - packet consumed by XDP and new buffer is allocated.
* false - packet should be passed to the stack.
*/
bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
struct xdp_buff *xdp, struct page *page, u8 **data_ptr,
unsigned int *len, u8 *event)
{
struct bpf_prog *xdp_prog = READ_ONCE(rxr->xdp_prog);
struct bnxt_tx_ring_info *txr;
struct bnxt_sw_rx_bd *rx_buf;
struct pci_dev *pdev;
dma_addr_t mapping;
u32 tx_needed = 1;
void *orig_data;
u32 tx_avail;
u32 offset;
u32 act;
if (!xdp_prog)
return false;
pdev = bp->pdev;
offset = bp->rx_offset;
txr = rxr->bnapi->tx_ring[0];
/* BNXT_RX_PAGE_MODE(bp) when XDP enabled */
orig_data = xdp->data;
act = bpf_prog_run_xdp(xdp_prog, xdp);
tx_avail = bnxt_tx_avail(bp, txr);
/* If the tx ring is not full, we must not update the rx producer yet
* because we may still be transmitting on some BDs.
*/
if (tx_avail != bp->tx_ring_size)
*event &= ~BNXT_RX_EVENT;
*len = xdp->data_end - xdp->data;
if (orig_data != xdp->data) {
offset = xdp->data - xdp->data_hard_start;
*data_ptr = xdp->data_hard_start + offset;
}
switch (act) {
case XDP_PASS:
return false;
case XDP_TX:
rx_buf = &rxr->rx_buf_ring[cons];
mapping = rx_buf->mapping - bp->rx_dma_offset;
if (unlikely(xdp_buff_has_frags(xdp))) {
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
tx_needed += sinfo->nr_frags;
}
if (tx_avail < tx_needed) {
trace_xdp_exception(bp->dev, xdp_prog, act);
bnxt_xdp_buff_frags_free(rxr, xdp);
bnxt_reuse_rx_data(rxr, cons, page);
return true;
}
dma_sync_single_for_device(&pdev->dev, mapping + offset, *len,
bp->rx_dir);
*event &= ~BNXT_RX_EVENT;
*event |= BNXT_TX_EVENT;
__bnxt_xmit_xdp(bp, txr, mapping + offset, *len,
NEXT_RX(rxr->rx_prod), xdp);
bnxt_reuse_rx_data(rxr, cons, page);
return true;
case XDP_REDIRECT:
/* if we are calling this here then we know that the
* redirect is coming from a frame received by the
* bnxt_en driver.
*/
/* if we are unable to allocate a new buffer, abort and reuse */
if (bnxt_alloc_rx_data(bp, rxr, rxr->rx_prod, GFP_ATOMIC)) {
trace_xdp_exception(bp->dev, xdp_prog, act);
bnxt_xdp_buff_frags_free(rxr, xdp);
bnxt_reuse_rx_data(rxr, cons, page);
return true;
}
if (xdp_do_redirect(bp->dev, xdp, xdp_prog)) {
trace_xdp_exception(bp->dev, xdp_prog, act);
page_pool_recycle_direct(rxr->page_pool, page);
return true;
}
*event |= BNXT_REDIRECT_EVENT;
break;
default:
bpf_warn_invalid_xdp_action(bp->dev, xdp_prog, act);
fallthrough;
case XDP_ABORTED:
trace_xdp_exception(bp->dev, xdp_prog, act);
fallthrough;
case XDP_DROP:
bnxt_xdp_buff_frags_free(rxr, xdp);
bnxt_reuse_rx_data(rxr, cons, page);
break;
}
return true;
}
int bnxt_xdp_xmit(struct net_device *dev, int num_frames,
struct xdp_frame **frames, u32 flags)
{
struct bnxt *bp = netdev_priv(dev);
struct bpf_prog *xdp_prog = READ_ONCE(bp->xdp_prog);
struct pci_dev *pdev = bp->pdev;
struct bnxt_tx_ring_info *txr;
dma_addr_t mapping;
int nxmit = 0;
int ring;
int i;
if (!test_bit(BNXT_STATE_OPEN, &bp->state) ||
!bp->tx_nr_rings_xdp ||
!xdp_prog)
return -EINVAL;
ring = smp_processor_id() % bp->tx_nr_rings_xdp;
txr = &bp->tx_ring[ring];
if (READ_ONCE(txr->dev_state) == BNXT_DEV_STATE_CLOSING)
return -EINVAL;
if (static_branch_unlikely(&bnxt_xdp_locking_key))
spin_lock(&txr->xdp_tx_lock);
for (i = 0; i < num_frames; i++) {
struct xdp_frame *xdp = frames[i];
if (!bnxt_tx_avail(bp, txr))
break;
mapping = dma_map_single(&pdev->dev, xdp->data, xdp->len,
DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, mapping))
break;
__bnxt_xmit_xdp_redirect(bp, txr, mapping, xdp->len, xdp);
nxmit++;
}
if (flags & XDP_XMIT_FLUSH) {
/* Sync BD data before updating doorbell */
wmb();
bnxt_db_write(bp, &txr->tx_db, txr->tx_prod);
}
if (static_branch_unlikely(&bnxt_xdp_locking_key))
spin_unlock(&txr->xdp_tx_lock);
return nxmit;
}
static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
{
struct net_device *dev = bp->dev;
int tx_xdp = 0, rc, tc;
struct bpf_prog *old;
netdev_assert_locked(dev);
if (prog && !prog->aux->xdp_has_frags &&
bp->dev->mtu > BNXT_MAX_PAGE_MODE_MTU) {
netdev_warn(dev, "MTU %d larger than %d without XDP frag support.\n",
bp->dev->mtu, BNXT_MAX_PAGE_MODE_MTU);
return -EOPNOTSUPP;
}
if (prog && bp->flags & BNXT_FLAG_HDS) {
netdev_warn(dev, "XDP is disallowed when HDS is enabled.\n");
return -EOPNOTSUPP;
}
if (!(bp->flags & BNXT_FLAG_SHARED_RINGS)) {
netdev_warn(dev, "ethtool rx/tx channels must be combined to support XDP.\n");
return -EOPNOTSUPP;
}
if (prog)
tx_xdp = bp->rx_nr_rings;
tc = bp->num_tc;
if (!tc)
tc = 1;
rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings,
true, tc, tx_xdp);
if (rc) {
netdev_warn(dev, "Unable to reserve enough TX rings to support XDP.\n");
return rc;
}
if (netif_running(dev))
bnxt_close_nic(bp, true, false);
old = xchg(&bp->xdp_prog, prog);
if (old)
bpf_prog_put(old);
if (prog) {
bnxt_set_rx_skb_mode(bp, true);
xdp_features_set_redirect_target_locked(dev, true);
} else {
xdp_features_clear_redirect_target_locked(dev);
bnxt_set_rx_skb_mode(bp, false);
}
bp->tx_nr_rings_xdp = tx_xdp;
bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp;
bnxt_set_cp_rings(bp, true);
bnxt_set_tpa_flags(bp);
bnxt_set_ring_params(bp);
if (netif_running(dev))
return bnxt_open_nic(bp, true, false);
return 0;
}
int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp)
{
struct bnxt *bp = netdev_priv(dev);
int rc;
switch (xdp->command) {
case XDP_SETUP_PROG:
rc = bnxt_xdp_set(bp, xdp->prog);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
struct sk_buff *
bnxt_xdp_build_skb(struct bnxt *bp, struct sk_buff *skb, u8 num_frags,
struct bnxt_rx_ring_info *rxr, struct xdp_buff *xdp)
{
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
if (!skb)
return NULL;
xdp_update_skb_frags_info(skb, num_frags, sinfo->xdp_frags_size,
rxr->rx_page_size * num_frags,
xdp_buff_get_skb_flags(xdp));
return skb;
}
int bnxt_xdp_rx_hash(const struct xdp_md *ctx, u32 *hash,
enum xdp_rss_hash_type *rss_type)
{
const struct bnxt_xdp_buff *xdp = (void *)ctx;
const struct rx_cmp_ext *rxcmp1 = xdp->rxcmp1;
const struct rx_cmp *rxcmp = xdp->rxcmp;
enum xdp_rss_hash_type hash_type = 0;
u32 itypes;
if (!rxcmp || !RX_CMP_HASH_VALID(rxcmp))
return -ENODATA;
*hash = le32_to_cpu(rxcmp->rx_cmp_rss_hash);
if (!rxcmp1) {
*rss_type = XDP_RSS_TYPE_L2;
return 0;
}
if (xdp->cmp_type == CMP_TYPE_RX_L2_CMP) {
itypes = RX_CMP_ITYPES(rxcmp);
if (rxcmp1->rx_cmp_flags2 &
cpu_to_le32(RX_CMP_FLAGS2_IP_TYPE)) {
hash_type |= XDP_RSS_TYPE_L3_IPV6;
} else {
hash_type |= XDP_RSS_TYPE_L3_IPV4;
}
switch (itypes) {
case RX_CMP_FLAGS_ITYPE_TCP:
hash_type |= XDP_RSS_L4 | XDP_RSS_L4_TCP;
break;
case RX_CMP_FLAGS_ITYPE_UDP:
hash_type |= XDP_RSS_L4 | XDP_RSS_L4_UDP;
break;
case RX_CMP_FLAGS_ITYPE_ICMP:
hash_type |= XDP_RSS_L4 | XDP_RSS_L4_ICMP;
break;
default:
break;
}
} else if (xdp->cmp_type == CMP_TYPE_RX_L2_V3_CMP) {
struct bnxt *bp = netdev_priv(xdp->xdp.rxq->dev);
if (rxcmp1->rx_cmp_flags2 & cpu_to_le32(RX_CMP_FLAGS2_IP_TYPE))
hash_type |= XDP_RSS_TYPE_L3_IPV6;
else
hash_type |= XDP_RSS_TYPE_L3_IPV4;
if (bnxt_rss_ext_op(bp, rxcmp) == PKT_HASH_TYPE_L4)
hash_type |= XDP_RSS_L4;
}
*rss_type = hash_type;
return 0;
}
@@ -0,0 +1,47 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2016-2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#ifndef BNXT_XDP_H
#define BNXT_XDP_H
DECLARE_STATIC_KEY_FALSE(bnxt_xdp_locking_key);
struct bnxt_xdp_buff {
struct xdp_buff xdp;
struct rx_cmp *rxcmp;
struct rx_cmp_ext *rxcmp1;
u8 cmp_type;
};
struct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
dma_addr_t mapping, u32 len,
struct xdp_buff *xdp);
void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int budget);
bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
struct xdp_buff *xdp, struct page *page, u8 **data_ptr,
unsigned int *len, u8 *event);
int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp);
int bnxt_xdp_xmit(struct net_device *dev, int num_frames,
struct xdp_frame **frames, u32 flags);
bool bnxt_xdp_attached(struct bnxt *bp, struct bnxt_rx_ring_info *rxr);
void bnxt_xdp_buff_init(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
u16 cons, u8 *data_ptr, unsigned int len,
struct xdp_buff *xdp);
void bnxt_xdp_buff_frags_free(struct bnxt_rx_ring_info *rxr,
struct xdp_buff *xdp);
struct sk_buff *bnxt_xdp_build_skb(struct bnxt *bp, struct sk_buff *skb,
u8 num_frags, struct bnxt_rx_ring_info *rxr,
struct xdp_buff *xdp);
int bnxt_xdp_rx_hash(const struct xdp_md *ctx, u32 *hash,
enum xdp_rss_hash_type *rss_type);
#endif