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,16 @@
# SPDX-License-Identifier: GPL-2.0
#
# Chelsio T4 driver
#
obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
cxgb4-objs := cxgb4_main.o l2t.o smt.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o \
cxgb4_uld.o srq.o sched.o cxgb4_filter.o cxgb4_tc_u32.o \
cxgb4_ptp.o cxgb4_tc_flower.o cxgb4_cudbg.o cxgb4_mps.o \
cudbg_common.o cudbg_lib.o cudbg_zlib.o cxgb4_tc_mqprio.o \
cxgb4_tc_matchall.o
cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o
cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
cxgb4-$(CONFIG_THERMAL) += cxgb4_thermal.o
@@ -0,0 +1,328 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
* Copyright (C) 2003-2014 Chelsio Communications. All rights reserved.
*
* Written by Deepak (deepak.s@chelsio.com)
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
* release for licensing terms and conditions.
*/
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/jhash.h>
#include <linux/if_vlan.h>
#include <net/addrconf.h>
#include "cxgb4.h"
#include "clip_tbl.h"
static inline unsigned int ipv4_clip_hash(struct clip_tbl *c, const u32 *key)
{
unsigned int clipt_size_half = c->clipt_size / 2;
return jhash_1word(*key, 0) % clipt_size_half;
}
static inline unsigned int ipv6_clip_hash(struct clip_tbl *d, const u32 *key)
{
unsigned int clipt_size_half = d->clipt_size / 2;
u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3];
return clipt_size_half +
(jhash_1word(xor, 0) % clipt_size_half);
}
static unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr,
u8 v6)
{
return v6 ? ipv6_clip_hash(ctbl, addr) :
ipv4_clip_hash(ctbl, addr);
}
static int clip6_get_mbox(const struct net_device *dev,
const struct in6_addr *lip)
{
struct adapter *adap = netdev2adap(dev);
struct fw_clip_cmd c;
memset(&c, 0, sizeof(c));
c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
c.alloc_to_len16 = htonl(FW_CLIP_CMD_ALLOC_F | FW_LEN16(c));
*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}
static int clip6_release_mbox(const struct net_device *dev,
const struct in6_addr *lip)
{
struct adapter *adap = netdev2adap(dev);
struct fw_clip_cmd c;
memset(&c, 0, sizeof(c));
c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) |
FW_CMD_REQUEST_F | FW_CMD_READ_F);
c.alloc_to_len16 = htonl(FW_CLIP_CMD_FREE_F | FW_LEN16(c));
*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}
int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
{
struct adapter *adap = netdev2adap(dev);
struct clip_tbl *ctbl = adap->clipt;
struct clip_entry *ce, *cte;
u32 *addr = (u32 *)lip;
int hash;
int ret = -1;
if (!ctbl)
return 0;
hash = clip_addr_hash(ctbl, addr, v6);
read_lock_bh(&ctbl->lock);
list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
if (cte->addr6.sin6_family == AF_INET6 && v6)
ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
sizeof(struct in6_addr));
else if (cte->addr.sin_family == AF_INET && !v6)
ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
sizeof(struct in_addr));
if (!ret) {
ce = cte;
read_unlock_bh(&ctbl->lock);
refcount_inc(&ce->refcnt);
return 0;
}
}
read_unlock_bh(&ctbl->lock);
write_lock_bh(&ctbl->lock);
if (!list_empty(&ctbl->ce_free_head)) {
ce = list_first_entry(&ctbl->ce_free_head,
struct clip_entry, list);
list_del_init(&ce->list);
spin_lock_init(&ce->lock);
refcount_set(&ce->refcnt, 0);
atomic_dec(&ctbl->nfree);
list_add_tail(&ce->list, &ctbl->hash_list[hash]);
if (v6) {
ce->addr6.sin6_family = AF_INET6;
memcpy(ce->addr6.sin6_addr.s6_addr,
lip, sizeof(struct in6_addr));
ret = clip6_get_mbox(dev, (const struct in6_addr *)lip);
if (ret) {
write_unlock_bh(&ctbl->lock);
dev_err(adap->pdev_dev,
"CLIP FW cmd failed with error %d, "
"Connections using %pI6c won't be "
"offloaded",
ret, ce->addr6.sin6_addr.s6_addr);
return ret;
}
} else {
ce->addr.sin_family = AF_INET;
memcpy((char *)(&ce->addr.sin_addr), lip,
sizeof(struct in_addr));
}
} else {
write_unlock_bh(&ctbl->lock);
dev_info(adap->pdev_dev, "CLIP table overflow, "
"Connections using %pI6c won't be offloaded",
(void *)lip);
return -ENOMEM;
}
write_unlock_bh(&ctbl->lock);
refcount_set(&ce->refcnt, 1);
return 0;
}
EXPORT_SYMBOL(cxgb4_clip_get);
void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6)
{
struct adapter *adap = netdev2adap(dev);
struct clip_tbl *ctbl = adap->clipt;
struct clip_entry *ce, *cte;
u32 *addr = (u32 *)lip;
int hash;
int ret = -1;
if (!ctbl)
return;
hash = clip_addr_hash(ctbl, addr, v6);
read_lock_bh(&ctbl->lock);
list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
if (cte->addr6.sin6_family == AF_INET6 && v6)
ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
sizeof(struct in6_addr));
else if (cte->addr.sin_family == AF_INET && !v6)
ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
sizeof(struct in_addr));
if (!ret) {
ce = cte;
read_unlock_bh(&ctbl->lock);
goto found;
}
}
read_unlock_bh(&ctbl->lock);
return;
found:
write_lock_bh(&ctbl->lock);
spin_lock_bh(&ce->lock);
if (refcount_dec_and_test(&ce->refcnt)) {
list_del_init(&ce->list);
list_add_tail(&ce->list, &ctbl->ce_free_head);
atomic_inc(&ctbl->nfree);
if (v6)
clip6_release_mbox(dev, (const struct in6_addr *)lip);
}
spin_unlock_bh(&ce->lock);
write_unlock_bh(&ctbl->lock);
}
EXPORT_SYMBOL(cxgb4_clip_release);
/* Retrieves IPv6 addresses from a root device (bond, vlan) associated with
* a physical device.
* The physical device reference is needed to send the actul CLIP command.
*/
static int cxgb4_update_dev_clip(struct net_device *root_dev,
struct net_device *dev)
{
struct inet6_dev *idev = NULL;
struct inet6_ifaddr *ifa;
int ret = 0;
idev = __in6_dev_get(root_dev);
if (!idev)
return ret;
read_lock_bh(&idev->lock);
list_for_each_entry(ifa, &idev->addr_list, if_list) {
ret = cxgb4_clip_get(dev, (const u32 *)ifa->addr.s6_addr, 1);
if (ret < 0)
break;
}
read_unlock_bh(&idev->lock);
return ret;
}
int cxgb4_update_root_dev_clip(struct net_device *dev)
{
struct net_device *root_dev = NULL;
int i, ret = 0;
/* First populate the real net device's IPv6 addresses */
ret = cxgb4_update_dev_clip(dev, dev);
if (ret)
return ret;
/* Parse all bond and vlan devices layered on top of the physical dev */
root_dev = netdev_master_upper_dev_get_rcu(dev);
if (root_dev) {
ret = cxgb4_update_dev_clip(root_dev, dev);
if (ret)
return ret;
}
for (i = 0; i < VLAN_N_VID; i++) {
root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i);
if (!root_dev)
continue;
ret = cxgb4_update_dev_clip(root_dev, dev);
if (ret)
break;
}
return ret;
}
EXPORT_SYMBOL(cxgb4_update_root_dev_clip);
int clip_tbl_show(struct seq_file *seq, void *v)
{
struct adapter *adapter = seq->private;
struct clip_tbl *ctbl = adapter->clipt;
struct clip_entry *ce;
char ip[60];
int i;
read_lock_bh(&ctbl->lock);
seq_puts(seq, "IP Address Users\n");
for (i = 0 ; i < ctbl->clipt_size; ++i) {
list_for_each_entry(ce, &ctbl->hash_list[i], list) {
ip[0] = '\0';
sprintf(ip, "%pISc", &ce->addr);
seq_printf(seq, "%-25s %u\n", ip,
refcount_read(&ce->refcnt));
}
}
seq_printf(seq, "Free clip entries : %d\n", atomic_read(&ctbl->nfree));
read_unlock_bh(&ctbl->lock);
return 0;
}
struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
unsigned int clipt_end)
{
struct clip_entry *cl_list;
struct clip_tbl *ctbl;
unsigned int clipt_size;
int i;
if (clipt_start >= clipt_end)
return NULL;
clipt_size = clipt_end - clipt_start + 1;
if (clipt_size < CLIPT_MIN_HASH_BUCKETS)
return NULL;
ctbl = kvzalloc_flex(*ctbl, hash_list, clipt_size);
if (!ctbl)
return NULL;
ctbl->clipt_start = clipt_start;
ctbl->clipt_size = clipt_size;
INIT_LIST_HEAD(&ctbl->ce_free_head);
atomic_set(&ctbl->nfree, clipt_size);
rwlock_init(&ctbl->lock);
for (i = 0; i < ctbl->clipt_size; ++i)
INIT_LIST_HEAD(&ctbl->hash_list[i]);
cl_list = kvzalloc_objs(struct clip_entry, clipt_size);
if (!cl_list) {
kvfree(ctbl);
return NULL;
}
ctbl->cl_list = (void *)cl_list;
for (i = 0; i < clipt_size; i++) {
INIT_LIST_HEAD(&cl_list[i].list);
list_add_tail(&cl_list[i].list, &ctbl->ce_free_head);
}
return ctbl;
}
void t4_cleanup_clip_tbl(struct adapter *adap)
{
struct clip_tbl *ctbl = adap->clipt;
if (ctbl) {
kvfree(ctbl->cl_list);
kvfree(ctbl);
}
}
EXPORT_SYMBOL(t4_cleanup_clip_tbl);
@@ -0,0 +1,45 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
* Copyright (C) 2003-2014 Chelsio Communications. All rights reserved.
*
* Written by Deepak (deepak.s@chelsio.com)
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
* release for licensing terms and conditions.
*/
#include <linux/refcount.h>
struct clip_entry {
spinlock_t lock; /* Hold while modifying clip reference */
refcount_t refcnt;
struct list_head list;
union {
struct sockaddr_in addr;
struct sockaddr_in6 addr6;
};
};
struct clip_tbl {
unsigned int clipt_start;
unsigned int clipt_size;
rwlock_t lock;
atomic_t nfree;
struct list_head ce_free_head;
void *cl_list;
struct list_head hash_list[] __counted_by(clipt_size);
};
enum {
CLIPT_MIN_HASH_BUCKETS = 2,
};
struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
unsigned int clipt_end);
int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6);
void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6);
int clip_tbl_show(struct seq_file *seq, void *v);
int cxgb4_update_root_dev_clip(struct net_device *dev);
void t4_cleanup_clip_tbl(struct adapter *adap);
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*/
#include "cxgb4.h"
#include "cudbg_if.h"
#include "cudbg_lib_common.h"
int cudbg_get_buff(struct cudbg_init *pdbg_init,
struct cudbg_buffer *pdbg_buff, u32 size,
struct cudbg_buffer *pin_buff)
{
u32 offset;
offset = pdbg_buff->offset;
if (offset + size > pdbg_buff->size)
return CUDBG_STATUS_NO_MEM;
if (pdbg_init->compress_type != CUDBG_COMPRESSION_NONE) {
if (size > pdbg_init->compress_buff_size)
return CUDBG_STATUS_NO_MEM;
pin_buff->data = (char *)pdbg_init->compress_buff;
pin_buff->offset = 0;
pin_buff->size = size;
return 0;
}
pin_buff->data = (char *)pdbg_buff->data + offset;
pin_buff->offset = offset;
pin_buff->size = size;
return 0;
}
void cudbg_put_buff(struct cudbg_init *pdbg_init,
struct cudbg_buffer *pin_buff)
{
/* Clear compression buffer for re-use */
if (pdbg_init->compress_type != CUDBG_COMPRESSION_NONE)
memset(pdbg_init->compress_buff, 0,
pdbg_init->compress_buff_size);
pin_buff->data = NULL;
pin_buff->offset = 0;
pin_buff->size = 0;
}
void cudbg_update_buff(struct cudbg_buffer *pin_buff,
struct cudbg_buffer *pout_buff)
{
/* We already write to buffer provided by ethool, so just
* increment offset to next free space.
*/
pout_buff->offset += pin_buff->size;
}
@@ -0,0 +1,352 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*/
#ifndef __CUDBG_ENTITY_H__
#define __CUDBG_ENTITY_H__
#define EDC0_FLAG 0
#define EDC1_FLAG 1
#define MC_FLAG 2
#define MC0_FLAG 3
#define MC1_FLAG 4
#define HMA_FLAG 5
#define CUDBG_ENTITY_SIGNATURE 0xCCEDB001
struct cudbg_mbox_log {
struct mbox_cmd entry;
u32 hi[MBOX_LEN / 8];
u32 lo[MBOX_LEN / 8];
};
struct cudbg_cim_qcfg {
u8 chip;
u16 base[CIM_NUM_IBQ + CIM_NUM_OBQ_T5];
u16 size[CIM_NUM_IBQ + CIM_NUM_OBQ_T5];
u16 thres[CIM_NUM_IBQ];
u32 obq_wr[2 * CIM_NUM_OBQ_T5];
u32 stat[4 * (CIM_NUM_IBQ + CIM_NUM_OBQ_T5)];
};
struct cudbg_rss_vf_conf {
u32 rss_vf_vfl;
u32 rss_vf_vfh;
};
struct cudbg_pm_stats {
u32 tx_cnt[T6_PM_NSTATS];
u32 rx_cnt[T6_PM_NSTATS];
u64 tx_cyc[T6_PM_NSTATS];
u64 rx_cyc[T6_PM_NSTATS];
};
struct cudbg_hw_sched {
u32 kbps[NTX_SCHED];
u32 ipg[NTX_SCHED];
u32 pace_tab[NTX_SCHED];
u32 mode;
u32 map;
};
#define SGE_QBASE_DATA_REG_NUM 4
struct sge_qbase_reg_field {
u32 reg_addr;
u32 reg_data[SGE_QBASE_DATA_REG_NUM];
/* Max supported PFs */
u32 pf_data_value[PCIE_FW_MASTER_M + 1][SGE_QBASE_DATA_REG_NUM];
/* Max supported VFs */
u32 vf_data_value[T6_VF_M + 1][SGE_QBASE_DATA_REG_NUM];
u32 vfcount; /* Actual number of max vfs in current configuration */
};
struct ireg_field {
u32 ireg_addr;
u32 ireg_data;
u32 ireg_local_offset;
u32 ireg_offset_range;
};
struct ireg_buf {
struct ireg_field tp_pio;
u32 outbuf[32];
};
struct cudbg_ulprx_la {
u32 data[ULPRX_LA_SIZE * 8];
u32 size;
};
struct cudbg_tp_la {
u32 size;
u32 mode;
u8 data[];
};
static const char * const cudbg_region[] = {
"DBQ contexts:", "IMSG contexts:", "FLM cache:", "TCBs:",
"Pstructs:", "Timers:", "Rx FL:", "Tx FL:", "Pstruct FL:",
"Tx payload:", "Rx payload:", "LE hash:", "iSCSI region:",
"TDDP region:", "TPT region:", "STAG region:", "RQ region:",
"RQUDP region:", "PBL region:", "TXPBL region:",
"DBVFIFO region:", "ULPRX state:", "ULPTX state:",
"On-chip queues:"
};
/* Memory region info relative to current memory (i.e. wrt 0). */
struct cudbg_region_info {
bool exist; /* Does region exists in current memory? */
u32 start; /* Start wrt 0 */
u32 end; /* End wrt 0 */
};
struct cudbg_mem_desc {
u32 base;
u32 limit;
u32 idx;
};
#define CUDBG_MEMINFO_REV 1
struct cudbg_meminfo {
struct cudbg_mem_desc avail[4];
struct cudbg_mem_desc mem[ARRAY_SIZE(cudbg_region) + 3];
u32 avail_c;
u32 mem_c;
u32 up_ram_lo;
u32 up_ram_hi;
u32 up_extmem2_lo;
u32 up_extmem2_hi;
u32 rx_pages_data[3];
u32 tx_pages_data[4];
u32 p_structs;
u32 reserved[12];
u32 port_used[4];
u32 port_alloc[4];
u32 loopback_used[NCHAN];
u32 loopback_alloc[NCHAN];
u32 p_structs_free_cnt;
u32 free_rx_cnt;
u32 free_tx_cnt;
};
struct cudbg_cim_pif_la {
int size;
u8 data[];
};
struct cudbg_clk_info {
u64 retransmit_min;
u64 retransmit_max;
u64 persist_timer_min;
u64 persist_timer_max;
u64 keepalive_idle_timer;
u64 keepalive_interval;
u64 initial_srtt;
u64 finwait2_timer;
u32 dack_timer;
u32 res;
u32 cclk_ps;
u32 tre;
u32 dack_re;
};
struct cudbg_tid_info_region {
u32 ntids;
u32 nstids;
u32 stid_base;
u32 hash_base;
u32 natids;
u32 nftids;
u32 ftid_base;
u32 aftid_base;
u32 aftid_end;
u32 sftid_base;
u32 nsftids;
u32 uotid_base;
u32 nuotids;
u32 sb;
u32 flags;
u32 le_db_conf;
u32 ip_users;
u32 ipv6_users;
u32 hpftid_base;
u32 nhpftids;
};
#define CUDBG_TID_INFO_REV 1
struct cudbg_tid_info_region_rev1 {
struct cudbg_ver_hdr ver_hdr;
struct cudbg_tid_info_region tid;
u32 tid_start;
u32 reserved[16];
};
#define CUDBG_LOWMEM_MAX_CTXT_QIDS 256
#define CUDBG_MAX_FL_QIDS 1024
struct cudbg_ch_cntxt {
u32 cntxt_type;
u32 cntxt_id;
u32 data[SGE_CTXT_SIZE / 4];
};
#define CUDBG_MAX_RPLC_SIZE 128
struct cudbg_mps_tcam {
u64 mask;
u32 rplc[8];
u32 idx;
u32 cls_lo;
u32 cls_hi;
u32 rplc_size;
u32 vniy;
u32 vnix;
u32 dip_hit;
u32 vlan_vld;
u32 repli;
u16 ivlan;
u8 addr[ETH_ALEN];
u8 lookup_type;
u8 port_num;
u8 reserved[2];
};
#define CUDBG_VPD_VER_ADDR 0x18c7
#define CUDBG_VPD_VER_LEN 2
struct cudbg_vpd_data {
u8 sn[SERNUM_LEN + 1];
u8 bn[PN_LEN + 1];
u8 na[MACADDR_LEN + 1];
u8 mn[ID_LEN + 1];
u16 fw_major;
u16 fw_minor;
u16 fw_micro;
u16 fw_build;
u32 scfg_vers;
u32 vpd_vers;
};
#define CUDBG_MAX_TCAM_TID 0x800
#define CUDBG_T6_CLIP 1536
#define CUDBG_MAX_TID_COMP_EN 6144
#define CUDBG_MAX_TID_COMP_DIS 3072
enum cudbg_le_entry_types {
LE_ET_UNKNOWN = 0,
LE_ET_TCAM_CON = 1,
LE_ET_TCAM_SERVER = 2,
LE_ET_TCAM_FILTER = 3,
LE_ET_TCAM_CLIP = 4,
LE_ET_TCAM_ROUTING = 5,
LE_ET_HASH_CON = 6,
LE_ET_INVALID_TID = 8,
};
struct cudbg_tcam {
u32 filter_start;
u32 server_start;
u32 clip_start;
u32 routing_start;
u32 tid_hash_base;
u32 max_tid;
};
struct cudbg_tid_data {
u32 tid;
u32 dbig_cmd;
u32 dbig_conf;
u32 dbig_rsp_stat;
u32 data[NUM_LE_DB_DBGI_RSP_DATA_INSTANCES];
};
#define CUDBG_NUM_ULPTX 11
#define CUDBG_NUM_ULPTX_READ 512
#define CUDBG_NUM_ULPTX_ASIC 6
#define CUDBG_NUM_ULPTX_ASIC_READ 128
#define CUDBG_ULPTX_LA_REV 1
struct cudbg_ulptx_la {
u32 rdptr[CUDBG_NUM_ULPTX];
u32 wrptr[CUDBG_NUM_ULPTX];
u32 rddata[CUDBG_NUM_ULPTX];
u32 rd_data[CUDBG_NUM_ULPTX][CUDBG_NUM_ULPTX_READ];
u32 rdptr_asic[CUDBG_NUM_ULPTX_ASIC_READ];
u32 rddata_asic[CUDBG_NUM_ULPTX_ASIC_READ][CUDBG_NUM_ULPTX_ASIC];
};
#define CUDBG_CHAC_PBT_ADDR 0x2800
#define CUDBG_CHAC_PBT_LRF 0x3000
#define CUDBG_CHAC_PBT_DATA 0x3800
#define CUDBG_PBT_DYNAMIC_ENTRIES 8
#define CUDBG_PBT_STATIC_ENTRIES 16
#define CUDBG_LRF_ENTRIES 8
#define CUDBG_PBT_DATA_ENTRIES 512
struct cudbg_pbt_tables {
u32 pbt_dynamic[CUDBG_PBT_DYNAMIC_ENTRIES];
u32 pbt_static[CUDBG_PBT_STATIC_ENTRIES];
u32 lrf_table[CUDBG_LRF_ENTRIES];
u32 pbt_data[CUDBG_PBT_DATA_ENTRIES];
};
enum cudbg_qdesc_qtype {
CUDBG_QTYPE_UNKNOWN = 0,
CUDBG_QTYPE_NIC_TXQ,
CUDBG_QTYPE_NIC_RXQ,
CUDBG_QTYPE_NIC_FLQ,
CUDBG_QTYPE_CTRLQ,
CUDBG_QTYPE_FWEVTQ,
CUDBG_QTYPE_INTRQ,
CUDBG_QTYPE_PTP_TXQ,
CUDBG_QTYPE_OFLD_TXQ,
CUDBG_QTYPE_RDMA_RXQ,
CUDBG_QTYPE_RDMA_FLQ,
CUDBG_QTYPE_RDMA_CIQ,
CUDBG_QTYPE_ISCSI_RXQ,
CUDBG_QTYPE_ISCSI_FLQ,
CUDBG_QTYPE_ISCSIT_RXQ,
CUDBG_QTYPE_ISCSIT_FLQ,
CUDBG_QTYPE_CRYPTO_TXQ,
CUDBG_QTYPE_CRYPTO_RXQ,
CUDBG_QTYPE_CRYPTO_FLQ,
CUDBG_QTYPE_TLS_RXQ,
CUDBG_QTYPE_TLS_FLQ,
CUDBG_QTYPE_ETHOFLD_TXQ,
CUDBG_QTYPE_ETHOFLD_RXQ,
CUDBG_QTYPE_ETHOFLD_FLQ,
CUDBG_QTYPE_MAX,
};
#define CUDBG_QDESC_REV 1
struct cudbg_qdesc_entry {
u32 data_size;
u32 qtype;
u32 qid;
u32 desc_size;
u32 num_desc;
u8 data[]; /* Must be last */
};
struct cudbg_qdesc_info {
u32 qdesc_entry_size;
u32 num_queues;
u8 data[]; /* Must be last */
};
#define IREG_NUM_ELEM 4
#define CUDBG_NUM_PCIE_CONFIG_REGS 0x61
#endif /* __CUDBG_ENTITY_H__ */
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*/
#ifndef __CUDBG_IF_H__
#define __CUDBG_IF_H__
/* Error codes */
#define CUDBG_STATUS_NO_MEM -19
#define CUDBG_STATUS_ENTITY_NOT_FOUND -24
#define CUDBG_STATUS_NOT_IMPLEMENTED -28
#define CUDBG_SYSTEM_ERROR -29
#define CUDBG_STATUS_CCLK_NOT_DEFINED -32
#define CUDBG_STATUS_PARTIAL_DATA -41
#define CUDBG_MAJOR_VERSION 1
#define CUDBG_MINOR_VERSION 14
enum cudbg_dbg_entity_type {
CUDBG_REG_DUMP = 1,
CUDBG_DEV_LOG = 2,
CUDBG_CIM_LA = 3,
CUDBG_CIM_MA_LA = 4,
CUDBG_CIM_QCFG = 5,
CUDBG_CIM_IBQ_TP0 = 6,
CUDBG_CIM_IBQ_TP1 = 7,
CUDBG_CIM_IBQ_ULP = 8,
CUDBG_CIM_IBQ_SGE0 = 9,
CUDBG_CIM_IBQ_SGE1 = 10,
CUDBG_CIM_IBQ_NCSI = 11,
CUDBG_CIM_OBQ_ULP0 = 12,
CUDBG_CIM_OBQ_ULP1 = 13,
CUDBG_CIM_OBQ_ULP2 = 14,
CUDBG_CIM_OBQ_ULP3 = 15,
CUDBG_CIM_OBQ_SGE = 16,
CUDBG_CIM_OBQ_NCSI = 17,
CUDBG_EDC0 = 18,
CUDBG_EDC1 = 19,
CUDBG_MC0 = 20,
CUDBG_MC1 = 21,
CUDBG_RSS = 22,
CUDBG_RSS_VF_CONF = 25,
CUDBG_PATH_MTU = 27,
CUDBG_PM_STATS = 30,
CUDBG_HW_SCHED = 31,
CUDBG_TP_INDIRECT = 36,
CUDBG_SGE_INDIRECT = 37,
CUDBG_ULPRX_LA = 41,
CUDBG_TP_LA = 43,
CUDBG_MEMINFO = 44,
CUDBG_CIM_PIF_LA = 45,
CUDBG_CLK = 46,
CUDBG_CIM_OBQ_RXQ0 = 47,
CUDBG_CIM_OBQ_RXQ1 = 48,
CUDBG_PCIE_INDIRECT = 50,
CUDBG_PM_INDIRECT = 51,
CUDBG_TID_INFO = 54,
CUDBG_PCIE_CONFIG = 55,
CUDBG_DUMP_CONTEXT = 56,
CUDBG_MPS_TCAM = 57,
CUDBG_VPD_DATA = 58,
CUDBG_LE_TCAM = 59,
CUDBG_CCTRL = 60,
CUDBG_MA_INDIRECT = 61,
CUDBG_ULPTX_LA = 62,
CUDBG_UP_CIM_INDIRECT = 64,
CUDBG_PBT_TABLE = 65,
CUDBG_MBOX_LOG = 66,
CUDBG_HMA_INDIRECT = 67,
CUDBG_HMA = 68,
CUDBG_QDESC = 70,
CUDBG_FLASH = 71,
CUDBG_MAX_ENTITY = 72,
};
struct cudbg_init {
struct adapter *adap; /* Pointer to adapter structure */
void *outbuf; /* Output buffer */
u32 outbuf_size; /* Output buffer size */
u8 compress_type; /* Type of compression to use */
void *compress_buff; /* Compression buffer */
u32 compress_buff_size; /* Compression buffer size */
void *workspace; /* Workspace for zlib */
};
static inline unsigned int cudbg_mbytes_to_bytes(unsigned int size)
{
return size * 1024 * 1024;
}
#endif /* __CUDBG_IF_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,283 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*/
#ifndef __CUDBG_LIB_H__
#define __CUDBG_LIB_H__
int cudbg_collect_reg_dump(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_fw_devlog(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_la(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_ma_la(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_qcfg(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_ibq_tp0(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_ibq_tp1(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_ibq_ulp(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_ibq_sge0(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_ibq_sge1(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_ibq_ncsi(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_obq_ulp0(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_obq_ulp1(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_obq_ulp2(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_obq_ulp3(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_obq_sge(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_obq_ncsi(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_edc0_meminfo(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_edc1_meminfo(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_mc0_meminfo(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_mc1_meminfo(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_rss(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_rss_vf_config(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_tp_indirect(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_path_mtu(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_pm_stats(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_hw_sched(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_sge_indirect(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_ulprx_la(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_tp_la(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_meminfo(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cim_pif_la(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_clk_info(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_obq_sge_rx_q0(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_obq_sge_rx_q1(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_pcie_indirect(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_pm_indirect(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_tid(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_pcie_config(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_dump_context(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_mps_tcam(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_vpd_data(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_le_tcam(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_cctrl(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_ma_indirect(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_ulptx_la(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_up_cim_indirect(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_pbt_tables(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_mbox_log(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_hma_indirect(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_hma_meminfo(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_qdesc(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
int cudbg_collect_flash(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
u32 cudbg_get_entity_length(struct adapter *adap, u32 entity);
struct cudbg_entity_hdr *cudbg_get_entity_hdr(void *outbuf, int i);
void cudbg_align_debug_buffer(struct cudbg_buffer *dbg_buff,
struct cudbg_entity_hdr *entity_hdr);
u32 cudbg_cim_obq_size(struct adapter *padap, int qid);
int cudbg_dump_context_size(struct adapter *padap);
int cudbg_fill_meminfo(struct adapter *padap,
struct cudbg_meminfo *meminfo_buff);
void cudbg_fill_le_tcam_info(struct adapter *padap,
struct cudbg_tcam *tcam_region);
void cudbg_fill_qdesc_num_and_size(const struct adapter *padap,
u32 *num, u32 *size);
static inline u32 cudbg_uld_txq_to_qtype(u32 uld)
{
switch (uld) {
case CXGB4_TX_OFLD:
return CUDBG_QTYPE_OFLD_TXQ;
case CXGB4_TX_CRYPTO:
return CUDBG_QTYPE_CRYPTO_TXQ;
}
return CUDBG_QTYPE_UNKNOWN;
}
static inline u32 cudbg_uld_rxq_to_qtype(u32 uld)
{
switch (uld) {
case CXGB4_ULD_RDMA:
return CUDBG_QTYPE_RDMA_RXQ;
case CXGB4_ULD_ISCSI:
return CUDBG_QTYPE_ISCSI_RXQ;
case CXGB4_ULD_ISCSIT:
return CUDBG_QTYPE_ISCSIT_RXQ;
case CXGB4_ULD_CRYPTO:
return CUDBG_QTYPE_CRYPTO_RXQ;
case CXGB4_ULD_TLS:
return CUDBG_QTYPE_TLS_RXQ;
}
return CUDBG_QTYPE_UNKNOWN;
}
static inline u32 cudbg_uld_flq_to_qtype(u32 uld)
{
switch (uld) {
case CXGB4_ULD_RDMA:
return CUDBG_QTYPE_RDMA_FLQ;
case CXGB4_ULD_ISCSI:
return CUDBG_QTYPE_ISCSI_FLQ;
case CXGB4_ULD_ISCSIT:
return CUDBG_QTYPE_ISCSIT_FLQ;
case CXGB4_ULD_CRYPTO:
return CUDBG_QTYPE_CRYPTO_FLQ;
case CXGB4_ULD_TLS:
return CUDBG_QTYPE_TLS_FLQ;
}
return CUDBG_QTYPE_UNKNOWN;
}
static inline u32 cudbg_uld_ciq_to_qtype(u32 uld)
{
switch (uld) {
case CXGB4_ULD_RDMA:
return CUDBG_QTYPE_RDMA_CIQ;
}
return CUDBG_QTYPE_UNKNOWN;
}
static inline void cudbg_fill_qdesc_txq(const struct sge_txq *txq,
enum cudbg_qdesc_qtype type,
struct cudbg_qdesc_entry *entry)
{
entry->qtype = type;
entry->qid = txq->cntxt_id;
entry->desc_size = sizeof(struct tx_desc);
entry->num_desc = txq->size;
entry->data_size = txq->size * sizeof(struct tx_desc);
memcpy(entry->data, txq->desc, entry->data_size);
}
static inline void cudbg_fill_qdesc_rxq(const struct sge_rspq *rxq,
enum cudbg_qdesc_qtype type,
struct cudbg_qdesc_entry *entry)
{
entry->qtype = type;
entry->qid = rxq->cntxt_id;
entry->desc_size = rxq->iqe_len;
entry->num_desc = rxq->size;
entry->data_size = rxq->size * rxq->iqe_len;
memcpy(entry->data, rxq->desc, entry->data_size);
}
static inline void cudbg_fill_qdesc_flq(const struct sge_fl *flq,
enum cudbg_qdesc_qtype type,
struct cudbg_qdesc_entry *entry)
{
entry->qtype = type;
entry->qid = flq->cntxt_id;
entry->desc_size = sizeof(__be64);
entry->num_desc = flq->size;
entry->data_size = flq->size * sizeof(__be64);
memcpy(entry->data, flq->desc, entry->data_size);
}
static inline
struct cudbg_qdesc_entry *cudbg_next_qdesc(struct cudbg_qdesc_entry *e)
{
return (struct cudbg_qdesc_entry *)
((u8 *)e + sizeof(*e) + e->data_size);
}
#endif /* __CUDBG_LIB_H__ */
@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*/
#ifndef __CUDBG_LIB_COMMON_H__
#define __CUDBG_LIB_COMMON_H__
#define CUDBG_SIGNATURE 67856866 /* CUDB in ascii */
enum cudbg_dump_type {
CUDBG_DUMP_TYPE_MINI = 1,
};
enum cudbg_compression_type {
CUDBG_COMPRESSION_NONE = 1,
CUDBG_COMPRESSION_ZLIB,
};
struct cudbg_hdr {
u32 signature;
u32 hdr_len;
u16 major_ver;
u16 minor_ver;
u32 data_len;
u32 hdr_flags;
u16 max_entities;
u8 chip_ver;
u8 dump_type:3;
u8 reserved1:1;
u8 compress_type:4;
u32 reserved[8];
};
struct cudbg_entity_hdr {
u32 entity_type;
u32 start_offset;
u32 size;
int hdr_flags;
u32 sys_warn;
u32 sys_err;
u8 num_pad;
u8 flag; /* bit 0 is used to indicate ext data */
u8 reserved1[2];
u32 next_ext_offset; /* pointer to next extended entity meta data */
u32 reserved[5];
};
struct cudbg_ver_hdr {
u32 signature;
u16 revision;
u16 size;
};
struct cudbg_buffer {
u32 size;
u32 offset;
char *data;
};
struct cudbg_error {
int sys_err;
int sys_warn;
int app_err;
};
#define CDUMP_MAX_COMP_BUF_SIZE ((64 * 1024) - 1)
#define CUDBG_CHUNK_SIZE ((CDUMP_MAX_COMP_BUF_SIZE / 1024) * 1024)
int cudbg_get_buff(struct cudbg_init *pdbg_init,
struct cudbg_buffer *pdbg_buff, u32 size,
struct cudbg_buffer *pin_buff);
void cudbg_put_buff(struct cudbg_init *pdbg_init,
struct cudbg_buffer *pin_buff);
void cudbg_update_buff(struct cudbg_buffer *pin_buff,
struct cudbg_buffer *pout_buff);
#endif /* __CUDBG_LIB_COMMON_H__ */
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018 Chelsio Communications. All rights reserved.
*/
#include <linux/zlib.h>
#include "cxgb4.h"
#include "cudbg_if.h"
#include "cudbg_lib_common.h"
#include "cudbg_zlib.h"
static int cudbg_get_compress_hdr(struct cudbg_buffer *pdbg_buff,
struct cudbg_buffer *pin_buff)
{
if (pdbg_buff->offset + sizeof(struct cudbg_compress_hdr) >
pdbg_buff->size)
return CUDBG_STATUS_NO_MEM;
pin_buff->data = (char *)pdbg_buff->data + pdbg_buff->offset;
pin_buff->offset = 0;
pin_buff->size = sizeof(struct cudbg_compress_hdr);
pdbg_buff->offset += sizeof(struct cudbg_compress_hdr);
return 0;
}
int cudbg_compress_buff(struct cudbg_init *pdbg_init,
struct cudbg_buffer *pin_buff,
struct cudbg_buffer *pout_buff)
{
struct cudbg_buffer temp_buff = { 0 };
struct z_stream_s compress_stream;
struct cudbg_compress_hdr *c_hdr;
int rc;
/* Write compression header to output buffer before compression */
rc = cudbg_get_compress_hdr(pout_buff, &temp_buff);
if (rc)
return rc;
c_hdr = (struct cudbg_compress_hdr *)temp_buff.data;
c_hdr->compress_id = CUDBG_ZLIB_COMPRESS_ID;
memset(&compress_stream, 0, sizeof(struct z_stream_s));
compress_stream.workspace = pdbg_init->workspace;
rc = zlib_deflateInit2(&compress_stream, Z_DEFAULT_COMPRESSION,
Z_DEFLATED, CUDBG_ZLIB_WIN_BITS,
CUDBG_ZLIB_MEM_LVL, Z_DEFAULT_STRATEGY);
if (rc != Z_OK)
return CUDBG_SYSTEM_ERROR;
compress_stream.next_in = pin_buff->data;
compress_stream.avail_in = pin_buff->size;
compress_stream.next_out = pout_buff->data + pout_buff->offset;
compress_stream.avail_out = pout_buff->size - pout_buff->offset;
rc = zlib_deflate(&compress_stream, Z_FINISH);
if (rc != Z_STREAM_END)
return CUDBG_SYSTEM_ERROR;
rc = zlib_deflateEnd(&compress_stream);
if (rc != Z_OK)
return CUDBG_SYSTEM_ERROR;
c_hdr->compress_size = compress_stream.total_out;
c_hdr->decompress_size = pin_buff->size;
pout_buff->offset += compress_stream.total_out;
return 0;
}
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2018 Chelsio Communications. All rights reserved.
*/
#ifndef __CUDBG_ZLIB_H__
#define __CUDBG_ZLIB_H__
#include <linux/zlib.h>
#define CUDBG_ZLIB_COMPRESS_ID 17
#define CUDBG_ZLIB_WIN_BITS 12
#define CUDBG_ZLIB_MEM_LVL 4
struct cudbg_compress_hdr {
u32 compress_id;
u64 decompress_size;
u64 compress_size;
u64 rsvd[32];
};
static inline int cudbg_get_workspace_size(void)
{
return zlib_deflate_workspacesize(CUDBG_ZLIB_WIN_BITS,
CUDBG_ZLIB_MEM_LVL);
}
int cudbg_compress_buff(struct cudbg_init *pdbg_init,
struct cudbg_buffer *pin_buff,
struct cudbg_buffer *pout_buff);
#endif /* __CUDBG_ZLIB_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,279 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*/
#include "t4_regs.h"
#include "cxgb4.h"
#include "cxgb4_cudbg.h"
#include "cudbg_zlib.h"
static const struct cxgb4_collect_entity cxgb4_collect_mem_dump[] = {
{ CUDBG_EDC0, cudbg_collect_edc0_meminfo },
{ CUDBG_EDC1, cudbg_collect_edc1_meminfo },
{ CUDBG_MC0, cudbg_collect_mc0_meminfo },
{ CUDBG_MC1, cudbg_collect_mc1_meminfo },
{ CUDBG_HMA, cudbg_collect_hma_meminfo },
};
static const struct cxgb4_collect_entity cxgb4_collect_hw_dump[] = {
{ CUDBG_MBOX_LOG, cudbg_collect_mbox_log },
{ CUDBG_QDESC, cudbg_collect_qdesc },
{ CUDBG_DEV_LOG, cudbg_collect_fw_devlog },
{ CUDBG_REG_DUMP, cudbg_collect_reg_dump },
{ CUDBG_CIM_LA, cudbg_collect_cim_la },
{ CUDBG_CIM_MA_LA, cudbg_collect_cim_ma_la },
{ CUDBG_CIM_QCFG, cudbg_collect_cim_qcfg },
{ CUDBG_CIM_IBQ_TP0, cudbg_collect_cim_ibq_tp0 },
{ CUDBG_CIM_IBQ_TP1, cudbg_collect_cim_ibq_tp1 },
{ CUDBG_CIM_IBQ_ULP, cudbg_collect_cim_ibq_ulp },
{ CUDBG_CIM_IBQ_SGE0, cudbg_collect_cim_ibq_sge0 },
{ CUDBG_CIM_IBQ_SGE1, cudbg_collect_cim_ibq_sge1 },
{ CUDBG_CIM_IBQ_NCSI, cudbg_collect_cim_ibq_ncsi },
{ CUDBG_CIM_OBQ_ULP0, cudbg_collect_cim_obq_ulp0 },
{ CUDBG_CIM_OBQ_ULP1, cudbg_collect_cim_obq_ulp1 },
{ CUDBG_CIM_OBQ_ULP2, cudbg_collect_cim_obq_ulp2 },
{ CUDBG_CIM_OBQ_ULP3, cudbg_collect_cim_obq_ulp3 },
{ CUDBG_CIM_OBQ_SGE, cudbg_collect_cim_obq_sge },
{ CUDBG_CIM_OBQ_NCSI, cudbg_collect_cim_obq_ncsi },
{ CUDBG_RSS, cudbg_collect_rss },
{ CUDBG_RSS_VF_CONF, cudbg_collect_rss_vf_config },
{ CUDBG_PATH_MTU, cudbg_collect_path_mtu },
{ CUDBG_PM_STATS, cudbg_collect_pm_stats },
{ CUDBG_HW_SCHED, cudbg_collect_hw_sched },
{ CUDBG_TP_INDIRECT, cudbg_collect_tp_indirect },
{ CUDBG_SGE_INDIRECT, cudbg_collect_sge_indirect },
{ CUDBG_ULPRX_LA, cudbg_collect_ulprx_la },
{ CUDBG_TP_LA, cudbg_collect_tp_la },
{ CUDBG_MEMINFO, cudbg_collect_meminfo },
{ CUDBG_CIM_PIF_LA, cudbg_collect_cim_pif_la },
{ CUDBG_CLK, cudbg_collect_clk_info },
{ CUDBG_CIM_OBQ_RXQ0, cudbg_collect_obq_sge_rx_q0 },
{ CUDBG_CIM_OBQ_RXQ1, cudbg_collect_obq_sge_rx_q1 },
{ CUDBG_PCIE_INDIRECT, cudbg_collect_pcie_indirect },
{ CUDBG_PM_INDIRECT, cudbg_collect_pm_indirect },
{ CUDBG_TID_INFO, cudbg_collect_tid },
{ CUDBG_PCIE_CONFIG, cudbg_collect_pcie_config },
{ CUDBG_DUMP_CONTEXT, cudbg_collect_dump_context },
{ CUDBG_MPS_TCAM, cudbg_collect_mps_tcam },
{ CUDBG_VPD_DATA, cudbg_collect_vpd_data },
{ CUDBG_LE_TCAM, cudbg_collect_le_tcam },
{ CUDBG_CCTRL, cudbg_collect_cctrl },
{ CUDBG_MA_INDIRECT, cudbg_collect_ma_indirect },
{ CUDBG_ULPTX_LA, cudbg_collect_ulptx_la },
{ CUDBG_UP_CIM_INDIRECT, cudbg_collect_up_cim_indirect },
{ CUDBG_PBT_TABLE, cudbg_collect_pbt_tables },
{ CUDBG_HMA_INDIRECT, cudbg_collect_hma_indirect },
};
static const struct cxgb4_collect_entity cxgb4_collect_flash_dump[] = {
{ CUDBG_FLASH, cudbg_collect_flash },
};
u32 cxgb4_get_dump_length(struct adapter *adap, u32 flag)
{
u32 i, entity;
u32 len = 0;
u32 wsize;
if (flag & CXGB4_ETH_DUMP_HW) {
for (i = 0; i < ARRAY_SIZE(cxgb4_collect_hw_dump); i++) {
entity = cxgb4_collect_hw_dump[i].entity;
len += cudbg_get_entity_length(adap, entity);
}
}
if (flag & CXGB4_ETH_DUMP_MEM) {
for (i = 0; i < ARRAY_SIZE(cxgb4_collect_mem_dump); i++) {
entity = cxgb4_collect_mem_dump[i].entity;
len += cudbg_get_entity_length(adap, entity);
}
}
if (flag & CXGB4_ETH_DUMP_FLASH)
len += adap->params.sf_size;
/* If compression is enabled, a smaller destination buffer is enough */
wsize = cudbg_get_workspace_size();
if (wsize && len > CUDBG_DUMP_BUFF_SIZE)
len = CUDBG_DUMP_BUFF_SIZE;
return len;
}
static void cxgb4_cudbg_collect_entity(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
const struct cxgb4_collect_entity *e_arr,
u32 arr_size, void *buf, u32 *tot_size)
{
struct cudbg_error cudbg_err = { 0 };
struct cudbg_entity_hdr *entity_hdr;
u32 i, total_size = 0;
int ret;
for (i = 0; i < arr_size; i++) {
const struct cxgb4_collect_entity *e = &e_arr[i];
entity_hdr = cudbg_get_entity_hdr(buf, e->entity);
entity_hdr->entity_type = e->entity;
entity_hdr->start_offset = dbg_buff->offset;
memset(&cudbg_err, 0, sizeof(struct cudbg_error));
ret = e->collect_cb(pdbg_init, dbg_buff, &cudbg_err);
if (ret) {
entity_hdr->size = 0;
dbg_buff->offset = entity_hdr->start_offset;
} else {
cudbg_align_debug_buffer(dbg_buff, entity_hdr);
}
/* Log error and continue with next entity */
if (cudbg_err.sys_err)
ret = CUDBG_SYSTEM_ERROR;
entity_hdr->hdr_flags = ret;
entity_hdr->sys_err = cudbg_err.sys_err;
entity_hdr->sys_warn = cudbg_err.sys_warn;
total_size += entity_hdr->size;
}
*tot_size += total_size;
}
static int cudbg_alloc_compress_buff(struct cudbg_init *pdbg_init)
{
u32 workspace_size;
workspace_size = cudbg_get_workspace_size();
pdbg_init->compress_buff = vzalloc(CUDBG_COMPRESS_BUFF_SIZE +
workspace_size);
if (!pdbg_init->compress_buff)
return -ENOMEM;
pdbg_init->compress_buff_size = CUDBG_COMPRESS_BUFF_SIZE;
pdbg_init->workspace = (u8 *)pdbg_init->compress_buff +
CUDBG_COMPRESS_BUFF_SIZE - workspace_size;
return 0;
}
static void cudbg_free_compress_buff(struct cudbg_init *pdbg_init)
{
vfree(pdbg_init->compress_buff);
}
int cxgb4_cudbg_collect(struct adapter *adap, void *buf, u32 *buf_size,
u32 flag)
{
struct cudbg_buffer dbg_buff = { 0 };
u32 size, min_size, total_size = 0;
struct cudbg_init cudbg_init;
struct cudbg_hdr *cudbg_hdr;
int rc;
size = *buf_size;
memset(&cudbg_init, 0, sizeof(struct cudbg_init));
cudbg_init.adap = adap;
cudbg_init.outbuf = buf;
cudbg_init.outbuf_size = size;
dbg_buff.data = buf;
dbg_buff.size = size;
dbg_buff.offset = 0;
cudbg_hdr = (struct cudbg_hdr *)buf;
cudbg_hdr->signature = CUDBG_SIGNATURE;
cudbg_hdr->hdr_len = sizeof(struct cudbg_hdr);
cudbg_hdr->major_ver = CUDBG_MAJOR_VERSION;
cudbg_hdr->minor_ver = CUDBG_MINOR_VERSION;
cudbg_hdr->max_entities = CUDBG_MAX_ENTITY;
cudbg_hdr->chip_ver = adap->params.chip;
cudbg_hdr->dump_type = CUDBG_DUMP_TYPE_MINI;
min_size = sizeof(struct cudbg_hdr) +
sizeof(struct cudbg_entity_hdr) *
cudbg_hdr->max_entities;
if (size < min_size)
return -ENOMEM;
rc = cudbg_get_workspace_size();
if (rc) {
/* Zlib available. So, use zlib deflate */
cudbg_init.compress_type = CUDBG_COMPRESSION_ZLIB;
rc = cudbg_alloc_compress_buff(&cudbg_init);
if (rc) {
/* Ignore error and continue without compression. */
dev_warn(adap->pdev_dev,
"Fail allocating compression buffer ret: %d. Continuing without compression.\n",
rc);
cudbg_init.compress_type = CUDBG_COMPRESSION_NONE;
rc = 0;
}
} else {
cudbg_init.compress_type = CUDBG_COMPRESSION_NONE;
}
cudbg_hdr->compress_type = cudbg_init.compress_type;
dbg_buff.offset += min_size;
total_size = dbg_buff.offset;
if (flag & CXGB4_ETH_DUMP_HW)
cxgb4_cudbg_collect_entity(&cudbg_init, &dbg_buff,
cxgb4_collect_hw_dump,
ARRAY_SIZE(cxgb4_collect_hw_dump),
buf,
&total_size);
if (flag & CXGB4_ETH_DUMP_MEM)
cxgb4_cudbg_collect_entity(&cudbg_init, &dbg_buff,
cxgb4_collect_mem_dump,
ARRAY_SIZE(cxgb4_collect_mem_dump),
buf,
&total_size);
if (flag & CXGB4_ETH_DUMP_FLASH)
cxgb4_cudbg_collect_entity(&cudbg_init, &dbg_buff,
cxgb4_collect_flash_dump,
ARRAY_SIZE(cxgb4_collect_flash_dump),
buf,
&total_size);
cudbg_free_compress_buff(&cudbg_init);
cudbg_hdr->data_len = total_size;
if (cudbg_init.compress_type != CUDBG_COMPRESSION_NONE)
*buf_size = size;
else
*buf_size = total_size;
return 0;
}
void cxgb4_init_ethtool_dump(struct adapter *adapter)
{
adapter->eth_dump.flag = CXGB4_ETH_DUMP_NONE;
adapter->eth_dump.version = adapter->params.fw_vers;
adapter->eth_dump.len = 0;
}
static int cxgb4_cudbg_vmcoredd_collect(struct vmcoredd_data *data, void *buf)
{
struct adapter *adap = container_of(data, struct adapter, vmcoredd);
u32 len = data->size;
return cxgb4_cudbg_collect(adap, buf, &len, CXGB4_ETH_DUMP_ALL);
}
int cxgb4_cudbg_vmcore_add_dump(struct adapter *adap)
{
struct vmcoredd_data *data = &adap->vmcoredd;
u32 len;
len = sizeof(struct cudbg_hdr) +
sizeof(struct cudbg_entity_hdr) * CUDBG_MAX_ENTITY;
len += CUDBG_DUMP_BUFF_SIZE;
data->size = len;
snprintf(data->dump_name, sizeof(data->dump_name), "%s_%s",
cxgb4_driver_name, adap->name);
data->vmcoredd_callback = cxgb4_cudbg_vmcoredd_collect;
return vmcore_add_device_dump(data);
}
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*/
#ifndef __CXGB4_CUDBG_H__
#define __CXGB4_CUDBG_H__
#include "cudbg_if.h"
#include "cudbg_lib_common.h"
#include "cudbg_entity.h"
#include "cudbg_lib.h"
#define CUDBG_DUMP_BUFF_SIZE (32 * 1024 * 1024) /* 32 MB */
#define CUDBG_COMPRESS_BUFF_SIZE (4 * 1024 * 1024) /* 4 MB */
typedef int (*cudbg_collect_callback_t)(struct cudbg_init *pdbg_init,
struct cudbg_buffer *dbg_buff,
struct cudbg_error *cudbg_err);
struct cxgb4_collect_entity {
enum cudbg_dbg_entity_type entity;
cudbg_collect_callback_t collect_cb;
};
enum CXGB4_ETHTOOL_DUMP_FLAGS {
CXGB4_ETH_DUMP_NONE = ETH_FW_DUMP_DISABLE,
CXGB4_ETH_DUMP_MEM = (1 << 0), /* On-Chip Memory Dumps */
CXGB4_ETH_DUMP_HW = (1 << 1), /* various FW and HW dumps */
CXGB4_ETH_DUMP_FLASH = (1 << 2), /* Dump flash memory */
};
#define CXGB4_ETH_DUMP_ALL (CXGB4_ETH_DUMP_MEM | CXGB4_ETH_DUMP_HW)
u32 cxgb4_get_dump_length(struct adapter *adap, u32 flag);
int cxgb4_cudbg_collect(struct adapter *adap, void *buf, u32 *buf_size,
u32 flag);
void cxgb4_init_ethtool_dump(struct adapter *adapter);
int cxgb4_cudbg_vmcore_add_dump(struct adapter *adap);
#endif /* __CXGB4_CUDBG_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,153 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2013-2014 Chelsio Communications. All rights reserved.
*
* Written by Anish Bhatt (anish@chelsio.com)
*/
#ifndef __CXGB4_DCB_H
#define __CXGB4_DCB_H
#include <linux/netdevice.h>
#include <linux/dcbnl.h>
#include <net/dcbnl.h>
#ifdef CONFIG_CHELSIO_T4_DCB
#define CXGB4_DCBX_FW_SUPPORT \
(DCB_CAP_DCBX_VER_CEE | \
DCB_CAP_DCBX_VER_IEEE | \
DCB_CAP_DCBX_LLD_MANAGED)
#define CXGB4_DCBX_HOST_SUPPORT \
(DCB_CAP_DCBX_VER_CEE | \
DCB_CAP_DCBX_VER_IEEE | \
DCB_CAP_DCBX_HOST)
#define CXGB4_MAX_PRIORITY CXGB4_MAX_DCBX_APP_SUPPORTED
#define CXGB4_MAX_TCS CXGB4_MAX_DCBX_APP_SUPPORTED
#define INIT_PORT_DCB_CMD(__pcmd, __port, __op, __action) \
do { \
memset(&(__pcmd), 0, sizeof(__pcmd)); \
(__pcmd).op_to_portid = \
cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | \
FW_CMD_REQUEST_F | \
FW_CMD_##__op##_F | \
FW_PORT_CMD_PORTID_V(__port)); \
(__pcmd).action_to_len16 = \
cpu_to_be32(FW_PORT_CMD_ACTION_V(__action) | \
FW_LEN16(pcmd)); \
} while (0)
#define INIT_PORT_DCB_READ_PEER_CMD(__pcmd, __port) \
INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_RECV)
#define INIT_PORT_DCB_READ_LOCAL_CMD(__pcmd, __port) \
INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_TRANS)
#define INIT_PORT_DCB_READ_SYNC_CMD(__pcmd, __port) \
INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_DET)
#define INIT_PORT_DCB_WRITE_CMD(__pcmd, __port) \
INIT_PORT_DCB_CMD(__pcmd, __port, EXEC, FW_PORT_ACTION_L2_DCB_CFG)
#define IEEE_FAUX_SYNC(__dev, __dcb) \
do { \
if ((__dcb)->dcb_version == FW_PORT_DCB_VER_IEEE) \
cxgb4_dcb_state_fsm((__dev), \
CXGB4_DCB_INPUT_FW_ALLSYNCED); \
} while (0)
/* States we can be in for a port's Data Center Bridging.
*/
enum cxgb4_dcb_state {
CXGB4_DCB_STATE_START, /* initial unknown state */
CXGB4_DCB_STATE_HOST, /* we're using Host DCB (if at all) */
CXGB4_DCB_STATE_FW_INCOMPLETE, /* using firmware DCB, incomplete */
CXGB4_DCB_STATE_FW_ALLSYNCED, /* using firmware DCB, all sync'ed */
};
/* Data Center Bridging state input for the Finite State Machine.
*/
enum cxgb4_dcb_state_input {
/* Input from the firmware.
*/
CXGB4_DCB_INPUT_FW_DISABLED, /* firmware DCB disabled */
CXGB4_DCB_INPUT_FW_ENABLED, /* firmware DCB enabled */
CXGB4_DCB_INPUT_FW_INCOMPLETE, /* firmware reports incomplete DCB */
CXGB4_DCB_INPUT_FW_ALLSYNCED, /* firmware reports all sync'ed */
};
/* Firmware DCB messages that we've received so far ...
*/
enum cxgb4_dcb_fw_msgs {
CXGB4_DCB_FW_PGID = 0x01,
CXGB4_DCB_FW_PGRATE = 0x02,
CXGB4_DCB_FW_PRIORATE = 0x04,
CXGB4_DCB_FW_PFC = 0x08,
CXGB4_DCB_FW_APP_ID = 0x10,
};
#define CXGB4_MAX_DCBX_APP_SUPPORTED 8
/* Data Center Bridging support;
*/
struct port_dcb_info {
enum cxgb4_dcb_state state; /* DCB State Machine */
enum cxgb4_dcb_fw_msgs msgs; /* DCB Firmware messages received */
unsigned int supported; /* OS DCB capabilities supported */
bool enabled; /* OS Enabled state */
/* Cached copies of DCB information sent by the firmware (in Host
* Native Endian format).
*/
u32 pgid; /* Priority Group[0..7] */
u8 dcb_version; /* Running DCBx version */
u8 pfcen; /* Priority Flow Control[0..7] */
u8 pg_num_tcs_supported; /* max PG Traffic Classes */
u8 pfc_num_tcs_supported; /* max PFC Traffic Classes */
u8 pgrate[8]; /* Priority Group Rate[0..7] */
u8 priorate[8]; /* Priority Rate[0..7] */
u8 tsa[8]; /* TSA Algorithm[0..7] */
struct app_priority { /* Application Information */
u8 user_prio_map; /* Priority Map bitfield */
u8 sel_field; /* Protocol ID interpretation */
u16 protocolid; /* Protocol ID */
} app_priority[CXGB4_MAX_DCBX_APP_SUPPORTED];
};
void cxgb4_dcb_state_init(struct net_device *);
void cxgb4_dcb_version_init(struct net_device *);
void cxgb4_dcb_reset(struct net_device *dev);
void cxgb4_dcb_state_fsm(struct net_device *, enum cxgb4_dcb_state_input);
void cxgb4_dcb_handle_fw_update(struct adapter *, const struct fw_port_cmd *);
extern const struct dcbnl_rtnl_ops cxgb4_dcb_ops;
static inline __u8 bitswap_1(unsigned char val)
{
return ((val & 0x80) >> 7) |
((val & 0x40) >> 5) |
((val & 0x20) >> 3) |
((val & 0x10) >> 1) |
((val & 0x08) << 1) |
((val & 0x04) << 3) |
((val & 0x02) << 5) |
((val & 0x01) << 7);
}
extern const char * const dcb_ver_array[];
#define CXGB4_DCB_ENABLED true
#else /* !CONFIG_CHELSIO_T4_DCB */
static inline void cxgb4_dcb_state_init(struct net_device *dev)
{
}
#define CXGB4_DCB_ENABLED false
#endif /* !CONFIG_CHELSIO_T4_DCB */
#endif /* __CXGB4_DCB_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,70 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_DEBUGFS_H
#define __CXGB4_DEBUGFS_H
#include <linux/export.h>
struct t4_debugfs_entry {
const char *name;
const struct file_operations *ops;
umode_t mode;
unsigned char data;
};
struct seq_tab {
int (*show)(struct seq_file *seq, void *v, int idx);
unsigned int rows; /* # of entries */
unsigned char width; /* size in bytes of each entry */
unsigned char skip_first; /* whether the first line is a header */
char data[]; /* the table data */
};
static inline unsigned int hex2val(char c)
{
return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10;
}
struct seq_tab *seq_open_tab(struct file *f, unsigned int rows,
unsigned int width, unsigned int have_header,
int (*show)(struct seq_file *seq, void *v, int i));
int t4_setup_debugfs(struct adapter *adap);
void add_debugfs_files(struct adapter *adap,
struct t4_debugfs_entry *files,
unsigned int nfiles);
int mem_open(struct inode *inode, struct file *file);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,120 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2015 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef CONFIG_CHELSIO_T4_FCOE
#include <scsi/fc/fc_fs.h>
#include <scsi/libfcoe.h>
#include "cxgb4.h"
bool cxgb_fcoe_sof_eof_supported(struct adapter *adap, struct sk_buff *skb)
{
struct fcoe_hdr *fcoeh = (struct fcoe_hdr *)skb_network_header(skb);
u8 sof = fcoeh->fcoe_sof;
u8 eof = 0;
if ((sof != FC_SOF_I3) && (sof != FC_SOF_N3)) {
dev_err(adap->pdev_dev, "Unsupported SOF 0x%x\n", sof);
return false;
}
skb_copy_bits(skb, skb->len - 4, &eof, 1);
if ((eof != FC_EOF_N) && (eof != FC_EOF_T)) {
dev_err(adap->pdev_dev, "Unsupported EOF 0x%x\n", eof);
return false;
}
return true;
}
/**
* cxgb_fcoe_enable - enable FCoE offload features
* @netdev: net device
*
* Returns 0 on success or -EINVAL on failure.
*/
int cxgb_fcoe_enable(struct net_device *netdev)
{
struct port_info *pi = netdev_priv(netdev);
struct adapter *adap = pi->adapter;
struct cxgb_fcoe *fcoe = &pi->fcoe;
if (is_t4(adap->params.chip))
return -EINVAL;
if (!(adap->flags & CXGB4_FULL_INIT_DONE))
return -EINVAL;
dev_info(adap->pdev_dev, "Enabling FCoE offload features\n");
netdev->features |= NETIF_F_FCOE_CRC;
netdev->vlan_features |= NETIF_F_FCOE_CRC;
netdev->fcoe_mtu = true;
netdev_features_change(netdev);
fcoe->flags |= CXGB_FCOE_ENABLED;
return 0;
}
/**
* cxgb_fcoe_disable - disable FCoE offload
* @netdev: net device
*
* Returns 0 on success or -EINVAL on failure.
*/
int cxgb_fcoe_disable(struct net_device *netdev)
{
struct port_info *pi = netdev_priv(netdev);
struct adapter *adap = pi->adapter;
struct cxgb_fcoe *fcoe = &pi->fcoe;
if (!(fcoe->flags & CXGB_FCOE_ENABLED))
return -EINVAL;
dev_info(adap->pdev_dev, "Disabling FCoE offload features\n");
fcoe->flags &= ~CXGB_FCOE_ENABLED;
netdev->features &= ~NETIF_F_FCOE_CRC;
netdev->vlan_features &= ~NETIF_F_FCOE_CRC;
netdev->fcoe_mtu = false;
netdev_features_change(netdev);
return 0;
}
#endif /* CONFIG_CHELSIO_T4_FCOE */
@@ -0,0 +1,57 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2015 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_FCOE_H__
#define __CXGB4_FCOE_H__
#ifdef CONFIG_CHELSIO_T4_FCOE
#define CXGB_FCOE_TXPKT_CSUM_START 28
#define CXGB_FCOE_TXPKT_CSUM_END 8
/* fcoe flags */
enum {
CXGB_FCOE_ENABLED = (1 << 0),
};
struct cxgb_fcoe {
u8 flags;
};
int cxgb_fcoe_enable(struct net_device *);
int cxgb_fcoe_disable(struct net_device *);
bool cxgb_fcoe_sof_eof_supported(struct adapter *, struct sk_buff *);
#endif /* CONFIG_CHELSIO_T4_FCOE */
#endif /* __CXGB4_FCOE_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,58 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_FILTER_H
#define __CXGB4_FILTER_H
#include "t4_msg.h"
#define WORD_MASK 0xffffffff
void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
void hash_filter_rpl(struct adapter *adap, const struct cpl_act_open_rpl *rpl);
void hash_del_filter_rpl(struct adapter *adap,
const struct cpl_abort_rpl_rss *rpl);
void clear_filter(struct adapter *adap, struct filter_entry *f);
int set_filter_wr(struct adapter *adapter, int fidx);
int delete_filter(struct adapter *adapter, unsigned int fidx);
int writable_filter(struct filter_entry *f);
void clear_all_filters(struct adapter *adapter);
void init_hash_filter(struct adapter *adap);
bool is_filter_exact_match(struct adapter *adap,
struct ch_filter_specification *fs);
void cxgb4_cleanup_ethtool_filters(struct adapter *adap);
int cxgb4_init_ethtool_filters(struct adapter *adap);
#endif /* __CXGB4_FILTER_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Chelsio Communications, Inc. All rights reserved. */
#include "cxgb4.h"
static int cxgb4_mps_ref_dec_by_mac(struct adapter *adap,
const u8 *addr, const u8 *mask)
{
u8 bitmask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct mps_entries_ref *mps_entry, *tmp;
int ret = -EINVAL;
spin_lock_bh(&adap->mps_ref_lock);
list_for_each_entry_safe(mps_entry, tmp, &adap->mps_ref, list) {
if (ether_addr_equal(mps_entry->addr, addr) &&
ether_addr_equal(mps_entry->mask, mask ? mask : bitmask)) {
if (!refcount_dec_and_test(&mps_entry->refcnt)) {
spin_unlock_bh(&adap->mps_ref_lock);
return -EBUSY;
}
list_del(&mps_entry->list);
kfree(mps_entry);
ret = 0;
break;
}
}
spin_unlock_bh(&adap->mps_ref_lock);
return ret;
}
static int cxgb4_mps_ref_inc(struct adapter *adap, const u8 *mac_addr,
u16 idx, const u8 *mask)
{
u8 bitmask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct mps_entries_ref *mps_entry;
int ret = 0;
spin_lock_bh(&adap->mps_ref_lock);
list_for_each_entry(mps_entry, &adap->mps_ref, list) {
if (mps_entry->idx == idx) {
refcount_inc(&mps_entry->refcnt);
goto unlock;
}
}
mps_entry = kzalloc_obj(*mps_entry, GFP_ATOMIC);
if (!mps_entry) {
ret = -ENOMEM;
goto unlock;
}
ether_addr_copy(mps_entry->mask, mask ? mask : bitmask);
ether_addr_copy(mps_entry->addr, mac_addr);
mps_entry->idx = idx;
refcount_set(&mps_entry->refcnt, 1);
list_add_tail(&mps_entry->list, &adap->mps_ref);
unlock:
spin_unlock_bh(&adap->mps_ref_lock);
return ret;
}
int cxgb4_free_mac_filt(struct adapter *adap, unsigned int viid,
unsigned int naddr, const u8 **addr, bool sleep_ok)
{
int ret, i;
for (i = 0; i < naddr; i++) {
if (!cxgb4_mps_ref_dec_by_mac(adap, addr[i], NULL)) {
ret = t4_free_mac_filt(adap, adap->mbox, viid,
1, &addr[i], sleep_ok);
if (ret < 0)
return ret;
}
}
/* return number of filters freed */
return naddr;
}
int cxgb4_alloc_mac_filt(struct adapter *adap, unsigned int viid,
bool free, unsigned int naddr, const u8 **addr,
u16 *idx, u64 *hash, bool sleep_ok)
{
int ret, i;
ret = t4_alloc_mac_filt(adap, adap->mbox, viid, free,
naddr, addr, idx, hash, sleep_ok);
if (ret < 0)
return ret;
for (i = 0; i < naddr; i++) {
if (idx[i] != 0xffff) {
if (cxgb4_mps_ref_inc(adap, addr[i], idx[i], NULL)) {
ret = -ENOMEM;
goto error;
}
}
}
goto out;
error:
cxgb4_free_mac_filt(adap, viid, naddr, addr, sleep_ok);
out:
/* Returns a negative error number or the number of filters allocated */
return ret;
}
int cxgb4_update_mac_filt(struct port_info *pi, unsigned int viid,
int *tcam_idx, const u8 *addr,
bool persistent, u8 *smt_idx)
{
int ret;
ret = cxgb4_change_mac(pi, viid, tcam_idx,
addr, persistent, smt_idx);
if (ret < 0)
return ret;
cxgb4_mps_ref_inc(pi->adapter, addr, *tcam_idx, NULL);
return ret;
}
int cxgb4_init_mps_ref_entries(struct adapter *adap)
{
spin_lock_init(&adap->mps_ref_lock);
INIT_LIST_HEAD(&adap->mps_ref);
return 0;
}
void cxgb4_free_mps_ref_entries(struct adapter *adap)
{
struct mps_entries_ref *mps_entry, *tmp;
if (list_empty(&adap->mps_ref))
return;
spin_lock(&adap->mps_ref_lock);
list_for_each_entry_safe(mps_entry, tmp, &adap->mps_ref, list) {
list_del(&mps_entry->list);
kfree(mps_entry);
}
spin_unlock(&adap->mps_ref_lock);
}
@@ -0,0 +1,468 @@
/*
* cxgb4_ptp.c:Chelsio PTP support for T5/T6
*
* Copyright (c) 2003-2017 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Written by: Atul Gupta (atul.gupta@chelsio.com)
*/
#include <linux/module.h>
#include <linux/net_tstamp.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/pps_kernel.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/ptp_classify.h>
#include <linux/udp.h>
#include "cxgb4.h"
#include "t4_hw.h"
#include "t4_regs.h"
#include "t4_msg.h"
#include "t4fw_api.h"
#include "cxgb4_ptp.h"
/**
* cxgb4_ptp_is_ptp_tx - determine whether TX packet is PTP or not
* @skb: skb of outgoing ptp request
*
*/
bool cxgb4_ptp_is_ptp_tx(struct sk_buff *skb)
{
struct udphdr *uh;
uh = udp_hdr(skb);
return skb->len >= PTP_MIN_LENGTH &&
skb->len <= PTP_IN_TRANSMIT_PACKET_MAXNUM &&
likely(skb->protocol == htons(ETH_P_IP)) &&
ip_hdr(skb)->protocol == IPPROTO_UDP &&
uh->dest == htons(PTP_EVENT_PORT);
}
bool is_ptp_enabled(struct sk_buff *skb, struct net_device *dev)
{
struct port_info *pi;
pi = netdev_priv(dev);
return (pi->ptp_enable && cxgb4_xmit_with_hwtstamp(skb) &&
cxgb4_ptp_is_ptp_tx(skb));
}
/**
* cxgb4_ptp_is_ptp_rx - determine whether RX packet is PTP or not
* @skb: skb of incoming ptp request
*
*/
bool cxgb4_ptp_is_ptp_rx(struct sk_buff *skb)
{
struct udphdr *uh = (struct udphdr *)(skb->data + ETH_HLEN +
IPV4_HLEN(skb->data));
return uh->dest == htons(PTP_EVENT_PORT) &&
uh->source == htons(PTP_EVENT_PORT);
}
/**
* cxgb4_ptp_read_hwstamp - read timestamp for TX event PTP message
* @adapter: board private structure
* @pi: port private structure
*
*/
void cxgb4_ptp_read_hwstamp(struct adapter *adapter, struct port_info *pi)
{
struct skb_shared_hwtstamps *skb_ts = NULL;
u64 tx_ts;
skb_ts = skb_hwtstamps(adapter->ptp_tx_skb);
tx_ts = t4_read_reg(adapter,
T5_PORT_REG(pi->port_id, MAC_PORT_TX_TS_VAL_LO));
tx_ts |= (u64)t4_read_reg(adapter,
T5_PORT_REG(pi->port_id,
MAC_PORT_TX_TS_VAL_HI)) << 32;
skb_ts->hwtstamp = ns_to_ktime(tx_ts);
skb_tstamp_tx(adapter->ptp_tx_skb, skb_ts);
dev_kfree_skb_any(adapter->ptp_tx_skb);
spin_lock(&adapter->ptp_lock);
adapter->ptp_tx_skb = NULL;
spin_unlock(&adapter->ptp_lock);
}
/**
* cxgb4_ptprx_timestamping - Enable Timestamp for RX PTP event message
* @pi: port private structure
* @port: pot number
* @mode: RX mode
*
*/
int cxgb4_ptprx_timestamping(struct port_info *pi, u8 port, u16 mode)
{
struct adapter *adapter = pi->adapter;
struct fw_ptp_cmd c;
int err;
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(port));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.init.sc = FW_PTP_SC_RXTIME_STAMP;
c.u.init.mode = cpu_to_be16(mode);
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
return err;
}
int cxgb4_ptp_txtype(struct adapter *adapter, u8 port)
{
struct fw_ptp_cmd c;
int err;
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(port));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.init.sc = FW_PTP_SC_TX_TYPE;
c.u.init.mode = cpu_to_be16(PTP_TS_NONE);
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
return err;
}
int cxgb4_ptp_redirect_rx_packet(struct adapter *adapter, struct port_info *pi)
{
struct sge *s = &adapter->sge;
struct sge_eth_rxq *receive_q = &s->ethrxq[pi->first_qset];
struct fw_ptp_cmd c;
int err;
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(pi->port_id));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.init.sc = FW_PTP_SC_RDRX_TYPE;
c.u.init.txchan = pi->tx_chan;
c.u.init.absid = cpu_to_be16(receive_q->rspq.abs_id);
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
return err;
}
/**
* cxgb4_ptp_adjfine - Adjust frequency of PHC cycle counter
* @ptp: ptp clock structure
* @scaled_ppm: Desired frequency in scaled parts per billion
*
* Adjust the frequency of the PHC cycle counter by the indicated amount from
* the base frequency.
*
* Scaled parts per million is ppm with a 16-bit binary fractional field.
*/
static int cxgb4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct adapter *adapter = (struct adapter *)container_of(ptp,
struct adapter, ptp_clock_info);
s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
struct fw_ptp_cmd c;
int err;
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(0));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.ts.sc = FW_PTP_SC_ADJ_FREQ;
c.u.ts.sign = (ppb < 0) ? 1 : 0;
if (ppb < 0)
ppb = -ppb;
c.u.ts.ppb = cpu_to_be32(ppb);
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
return err;
}
/**
* cxgb4_ptp_fineadjtime - Shift the time of the hardware clock
* @adapter: board private structure
* @delta: Desired change in nanoseconds
*
* Adjust the timer by resetting the timecounter structure.
*/
static int cxgb4_ptp_fineadjtime(struct adapter *adapter, s64 delta)
{
struct fw_ptp_cmd c;
int err;
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(0));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.ts.sc = FW_PTP_SC_ADJ_FTIME;
c.u.ts.sign = (delta < 0) ? 1 : 0;
if (delta < 0)
delta = -delta;
c.u.ts.tm = cpu_to_be64(delta);
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
return err;
}
/**
* cxgb4_ptp_adjtime - Shift the time of the hardware clock
* @ptp: ptp clock structure
* @delta: Desired change in nanoseconds
*
* Adjust the timer by resetting the timecounter structure.
*/
static int cxgb4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct adapter *adapter =
(struct adapter *)container_of(ptp, struct adapter,
ptp_clock_info);
struct fw_ptp_cmd c;
s64 sign = 1;
int err;
if (delta < 0)
sign = -1;
if (delta * sign > PTP_CLOCK_MAX_ADJTIME) {
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(0));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.ts.sc = FW_PTP_SC_ADJ_TIME;
c.u.ts.sign = (delta < 0) ? 1 : 0;
if (delta < 0)
delta = -delta;
c.u.ts.tm = cpu_to_be64(delta);
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
} else {
err = cxgb4_ptp_fineadjtime(adapter, delta);
}
return err;
}
/**
* cxgb4_ptp_gettime - Reads the current time from the hardware clock
* @ptp: ptp clock structure
* @ts: timespec structure to hold the current time value
*
* Read the timecounter and return the correct value in ns after converting
* it into a struct timespec.
*/
static int cxgb4_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct adapter *adapter = container_of(ptp, struct adapter,
ptp_clock_info);
u64 ns;
ns = t4_read_reg(adapter, T5_PORT_REG(0, MAC_PORT_PTP_SUM_LO_A));
ns |= (u64)t4_read_reg(adapter,
T5_PORT_REG(0, MAC_PORT_PTP_SUM_HI_A)) << 32;
/* convert to timespec*/
*ts = ns_to_timespec64(ns);
return 0;
}
/**
* cxgb4_ptp_settime - Set the current time on the hardware clock
* @ptp: ptp clock structure
* @ts: timespec containing the new time for the cycle counter
*
* Reset value to new base value instead of the kernel
* wall timer value.
*/
static int cxgb4_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct adapter *adapter = (struct adapter *)container_of(ptp,
struct adapter, ptp_clock_info);
struct fw_ptp_cmd c;
u64 ns;
int err;
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(0));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.ts.sc = FW_PTP_SC_SET_TIME;
ns = timespec64_to_ns(ts);
c.u.ts.tm = cpu_to_be64(ns);
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
return err;
}
static void cxgb4_init_ptp_timer(struct adapter *adapter)
{
struct fw_ptp_cmd c;
int err;
memset(&c, 0, sizeof(c));
c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
FW_CMD_REQUEST_F |
FW_CMD_WRITE_F |
FW_PTP_CMD_PORTID_V(0));
c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
c.u.scmd.sc = FW_PTP_SC_INIT_TIMER;
err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL);
if (err < 0)
dev_err(adapter->pdev_dev,
"PTP: %s error %d\n", __func__, -err);
}
/**
* cxgb4_ptp_enable - enable or disable an ancillary feature
* @ptp: ptp clock structure
* @request: Desired resource to enable or disable
* @on: Caller passes one to enable or zero to disable
*
* Enable (or disable) ancillary features of the PHC subsystem.
* Currently, no ancillary features are supported.
*/
static int cxgb4_ptp_enable(struct ptp_clock_info __always_unused *ptp,
struct ptp_clock_request __always_unused *request,
int __always_unused on)
{
return -ENOTSUPP;
}
static const struct ptp_clock_info cxgb4_ptp_clock_info = {
.owner = THIS_MODULE,
.name = "cxgb4_clock",
.max_adj = MAX_PTP_FREQ_ADJ,
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
.pps = 0,
.adjfine = cxgb4_ptp_adjfine,
.adjtime = cxgb4_ptp_adjtime,
.gettime64 = cxgb4_ptp_gettime,
.settime64 = cxgb4_ptp_settime,
.enable = cxgb4_ptp_enable,
};
/**
* cxgb4_ptp_init - initialize PTP for devices which support it
* @adapter: board private structure
*
* This function performs the required steps for enabling PTP support.
*/
void cxgb4_ptp_init(struct adapter *adapter)
{
struct timespec64 now;
/* no need to create a clock device if we already have one */
if (!IS_ERR_OR_NULL(adapter->ptp_clock))
return;
adapter->ptp_tx_skb = NULL;
adapter->ptp_clock_info = cxgb4_ptp_clock_info;
spin_lock_init(&adapter->ptp_lock);
adapter->ptp_clock = ptp_clock_register(&adapter->ptp_clock_info,
&adapter->pdev->dev);
if (IS_ERR_OR_NULL(adapter->ptp_clock)) {
adapter->ptp_clock = NULL;
dev_err(adapter->pdev_dev,
"PTP %s Clock registration has failed\n", __func__);
return;
}
now = ktime_to_timespec64(ktime_get_real());
cxgb4_init_ptp_timer(adapter);
if (cxgb4_ptp_settime(&adapter->ptp_clock_info, &now) < 0) {
ptp_clock_unregister(adapter->ptp_clock);
adapter->ptp_clock = NULL;
}
}
/**
* cxgb4_ptp_stop - disable PTP device and stop the overflow check
* @adapter: board private structure
*
* Stop the PTP support.
*/
void cxgb4_ptp_stop(struct adapter *adapter)
{
if (adapter->ptp_tx_skb) {
dev_kfree_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
}
if (adapter->ptp_clock) {
ptp_clock_unregister(adapter->ptp_clock);
adapter->ptp_clock = NULL;
}
}
@@ -0,0 +1,74 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2017 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_PTP_H__
#define __CXGB4_PTP_H__
/* Maximum parts-per-billion adjustment that is acceptable */
#define MAX_PTP_FREQ_ADJ 1000000
#define PTP_CLOCK_MAX_ADJTIME 10000000 /* 10 ms */
#define PTP_MIN_LENGTH 63
#define PTP_IN_TRANSMIT_PACKET_MAXNUM 240
#define PTP_EVENT_PORT 319
enum ptp_rx_filter_mode {
PTP_TS_NONE = 0,
PTP_TS_L2,
PTP_TS_L4,
PTP_TS_L2_L4
};
struct port_info;
static inline bool cxgb4_xmit_with_hwtstamp(struct sk_buff *skb)
{
return skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP;
}
static inline void cxgb4_xmit_hwtstamp_pending(struct sk_buff *skb)
{
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
}
void cxgb4_ptp_init(struct adapter *adap);
void cxgb4_ptp_stop(struct adapter *adap);
bool cxgb4_ptp_is_ptp_tx(struct sk_buff *skb);
bool cxgb4_ptp_is_ptp_rx(struct sk_buff *skb);
int cxgb4_ptprx_timestamping(struct port_info *pi, u8 port, u16 mode);
int cxgb4_ptp_redirect_rx_packet(struct adapter *adap, struct port_info *pi);
int cxgb4_ptp_txtype(struct adapter *adap, u8 port_id);
void cxgb4_ptp_read_hwstamp(struct adapter *adap, struct port_info *pi);
bool is_ptp_enabled(struct sk_buff *skb, struct net_device *dev);
#endif /* __CXGB4_PTP_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,148 @@
/*
* This file is part of the Chelsio T4/T5/T6 Ethernet driver for Linux.
*
* Copyright (c) 2017 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_TC_FLOWER_H
#define __CXGB4_TC_FLOWER_H
#include <net/pkt_cls.h>
struct ch_tc_flower_stats {
u64 prev_packet_count;
u64 packet_count;
u64 byte_count;
u64 last_used;
};
struct ch_tc_flower_entry {
struct ch_filter_specification fs;
struct ch_tc_flower_stats stats;
unsigned long tc_flower_cookie;
struct rhash_head node;
struct rcu_head rcu;
spinlock_t lock; /* lock for stats */
u32 filter_id;
};
enum {
ETH_DMAC_31_0, /* dmac bits 0.. 31 */
ETH_DMAC_47_32, /* dmac bits 32..47 */
ETH_SMAC_15_0, /* smac bits 0.. 15 */
ETH_SMAC_47_16, /* smac bits 16..47 */
IP4_SRC, /* 32-bit IPv4 src */
IP4_DST, /* 32-bit IPv4 dst */
IP6_SRC_31_0, /* src bits 0.. 31 */
IP6_SRC_63_32, /* src bits 63.. 32 */
IP6_SRC_95_64, /* src bits 95.. 64 */
IP6_SRC_127_96, /* src bits 127..96 */
IP6_DST_31_0, /* dst bits 0.. 31 */
IP6_DST_63_32, /* dst bits 63.. 32 */
IP6_DST_95_64, /* dst bits 95.. 64 */
IP6_DST_127_96, /* dst bits 127..96 */
TCP_SPORT, /* 16-bit TCP sport */
TCP_DPORT, /* 16-bit TCP dport */
UDP_SPORT, /* 16-bit UDP sport */
UDP_DPORT, /* 16-bit UDP dport */
};
struct ch_tc_pedit_fields {
u8 field;
u8 size;
u32 offset;
};
#define PEDIT_FIELDS(type, field, size, fs_field, offset) \
{ type## field, size, \
offsetof(struct ch_filter_specification, fs_field) + (offset) }
#define PEDIT_ETH_DMAC_MASK 0xffff
#define PEDIT_TCP_UDP_SPORT_MASK 0xffff
#define PEDIT_ETH_DMAC_31_0 0x0
#define PEDIT_ETH_DMAC_47_32_SMAC_15_0 0x4
#define PEDIT_ETH_SMAC_47_16 0x8
#define PEDIT_IP4_SRC 0xC
#define PEDIT_IP4_DST 0x10
#define PEDIT_IP6_SRC_31_0 0x8
#define PEDIT_IP6_SRC_63_32 0xC
#define PEDIT_IP6_SRC_95_64 0x10
#define PEDIT_IP6_SRC_127_96 0x14
#define PEDIT_IP6_DST_31_0 0x18
#define PEDIT_IP6_DST_63_32 0x1C
#define PEDIT_IP6_DST_95_64 0x20
#define PEDIT_IP6_DST_127_96 0x24
#define PEDIT_TCP_SPORT_DPORT 0x0
#define PEDIT_UDP_SPORT_DPORT 0x0
enum cxgb4_action_natmode_flags {
CXGB4_ACTION_NATMODE_NONE = 0,
CXGB4_ACTION_NATMODE_DIP = (1 << 0),
CXGB4_ACTION_NATMODE_SIP = (1 << 1),
CXGB4_ACTION_NATMODE_DPORT = (1 << 2),
CXGB4_ACTION_NATMODE_SPORT = (1 << 3),
};
/* TC PEDIT action to NATMODE translation entry */
struct cxgb4_natmode_config {
enum chip_type chip;
u8 flags;
u8 natmode;
};
void cxgb4_process_flow_actions(struct net_device *in,
struct flow_action *actions,
struct ch_filter_specification *fs);
int cxgb4_validate_flow_actions(struct net_device *dev,
struct flow_action *actions,
struct netlink_ext_ack *extack,
u8 matchall_filter);
int cxgb4_tc_flower_replace(struct net_device *dev,
struct flow_cls_offload *cls);
int cxgb4_tc_flower_destroy(struct net_device *dev,
struct flow_cls_offload *cls);
int cxgb4_tc_flower_stats(struct net_device *dev,
struct flow_cls_offload *cls);
int cxgb4_flow_rule_replace(struct net_device *dev, struct flow_rule *rule,
u32 tc_prio, struct netlink_ext_ack *extack,
struct ch_filter_specification *fs, u32 *tid);
int cxgb4_flow_rule_destroy(struct net_device *dev, u32 tc_prio,
struct ch_filter_specification *fs, int tid);
int cxgb4_init_tc_flower(struct adapter *adap);
void cxgb4_cleanup_tc_flower(struct adapter *adap);
#endif /* __CXGB4_TC_FLOWER_H */
@@ -0,0 +1,569 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2019 Chelsio Communications. All rights reserved. */
#include "cxgb4.h"
#include "cxgb4_tc_matchall.h"
#include "sched.h"
#include "cxgb4_uld.h"
#include "cxgb4_filter.h"
#include "cxgb4_tc_flower.h"
static int cxgb4_policer_validate(const struct flow_action *action,
const struct flow_action_entry *act,
struct netlink_ext_ack *extack)
{
if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when exceed action is not drop");
return -EOPNOTSUPP;
}
if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when conform action is not pipe or ok");
return -EOPNOTSUPP;
}
if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT &&
!flow_action_is_last_entry(action, act)) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when conform action is ok, but action is not last");
return -EOPNOTSUPP;
}
if (act->police.peakrate_bytes_ps ||
act->police.avrate || act->police.overhead) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when peakrate/avrate/overhead is configured");
return -EOPNOTSUPP;
}
if (act->police.rate_pkt_ps) {
NL_SET_ERR_MSG_MOD(extack,
"QoS offload not support packets per second");
return -EOPNOTSUPP;
}
return 0;
}
static int cxgb4_matchall_egress_validate(struct net_device *dev,
struct tc_cls_matchall_offload *cls)
{
struct netlink_ext_ack *extack = cls->common.extack;
struct flow_action *actions = &cls->rule->action;
struct port_info *pi = netdev2pinfo(dev);
struct flow_action_entry *entry;
struct ch_sched_queue qe;
struct ch_sched_class *e;
u64 max_link_rate;
u32 i, speed;
int ret;
if (!flow_action_has_entries(actions)) {
NL_SET_ERR_MSG_MOD(extack,
"Egress MATCHALL offload needs at least 1 policing action");
return -EINVAL;
} else if (!flow_offload_has_one_action(actions)) {
NL_SET_ERR_MSG_MOD(extack,
"Egress MATCHALL offload only supports 1 policing action");
return -EINVAL;
} else if (pi->tc_block_shared) {
NL_SET_ERR_MSG_MOD(extack,
"Egress MATCHALL offload not supported with shared blocks");
return -EINVAL;
}
ret = t4_get_link_params(pi, NULL, &speed, NULL);
if (ret) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to get max speed supported by the link");
return -EINVAL;
}
/* Convert from Mbps to bps */
max_link_rate = (u64)speed * 1000 * 1000;
flow_action_for_each(i, entry, actions) {
switch (entry->id) {
case FLOW_ACTION_POLICE:
ret = cxgb4_policer_validate(actions, entry, extack);
if (ret)
return ret;
/* Convert bytes per second to bits per second */
if (entry->police.rate_bytes_ps * 8 > max_link_rate) {
NL_SET_ERR_MSG_MOD(extack,
"Specified policing max rate is larger than underlying link speed");
return -ERANGE;
}
break;
default:
NL_SET_ERR_MSG_MOD(extack,
"Only policing action supported with Egress MATCHALL offload");
return -EOPNOTSUPP;
}
}
for (i = 0; i < pi->nqsets; i++) {
memset(&qe, 0, sizeof(qe));
qe.queue = i;
e = cxgb4_sched_queue_lookup(dev, &qe);
if (e && e->info.u.params.level != SCHED_CLASS_LEVEL_CH_RL) {
NL_SET_ERR_MSG_MOD(extack,
"Some queues are already bound to different class");
return -EBUSY;
}
}
return 0;
}
static int cxgb4_matchall_tc_bind_queues(struct net_device *dev, u32 tc)
{
struct port_info *pi = netdev2pinfo(dev);
struct ch_sched_queue qe;
int ret;
u32 i;
for (i = 0; i < pi->nqsets; i++) {
qe.queue = i;
qe.class = tc;
ret = cxgb4_sched_class_bind(dev, &qe, SCHED_QUEUE);
if (ret)
goto out_free;
}
return 0;
out_free:
while (i--) {
qe.queue = i;
qe.class = SCHED_CLS_NONE;
cxgb4_sched_class_unbind(dev, &qe, SCHED_QUEUE);
}
return ret;
}
static void cxgb4_matchall_tc_unbind_queues(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
struct ch_sched_queue qe;
u32 i;
for (i = 0; i < pi->nqsets; i++) {
qe.queue = i;
qe.class = SCHED_CLS_NONE;
cxgb4_sched_class_unbind(dev, &qe, SCHED_QUEUE);
}
}
static int cxgb4_matchall_alloc_tc(struct net_device *dev,
struct tc_cls_matchall_offload *cls)
{
struct ch_sched_params p = {
.type = SCHED_CLASS_TYPE_PACKET,
.u.params.level = SCHED_CLASS_LEVEL_CH_RL,
.u.params.mode = SCHED_CLASS_MODE_CLASS,
.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS,
.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS,
.u.params.class = SCHED_CLS_NONE,
.u.params.minrate = 0,
.u.params.weight = 0,
.u.params.pktsize = dev->mtu,
};
struct netlink_ext_ack *extack = cls->common.extack;
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct flow_action_entry *entry;
struct ch_sched_class *e;
int ret;
u32 i;
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
flow_action_for_each(i, entry, &cls->rule->action)
if (entry->id == FLOW_ACTION_POLICE)
break;
ret = cxgb4_policer_validate(&cls->rule->action, entry, extack);
if (ret)
return ret;
/* Convert from bytes per second to Kbps */
p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000);
p.u.params.channel = pi->tx_chan;
e = cxgb4_sched_class_alloc(dev, &p);
if (!e) {
NL_SET_ERR_MSG_MOD(extack,
"No free traffic class available for policing action");
return -ENOMEM;
}
ret = cxgb4_matchall_tc_bind_queues(dev, e->idx);
if (ret) {
NL_SET_ERR_MSG_MOD(extack,
"Could not bind queues to traffic class");
goto out_free;
}
tc_port_matchall->egress.hwtc = e->idx;
tc_port_matchall->egress.cookie = cls->cookie;
tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_ENABLED;
return 0;
out_free:
cxgb4_sched_class_free(dev, e->idx);
return ret;
}
static void cxgb4_matchall_free_tc(struct net_device *dev)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
cxgb4_matchall_tc_unbind_queues(dev);
cxgb4_sched_class_free(dev, tc_port_matchall->egress.hwtc);
tc_port_matchall->egress.hwtc = SCHED_CLS_NONE;
tc_port_matchall->egress.cookie = 0;
tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_DISABLED;
}
static int cxgb4_matchall_mirror_alloc(struct net_device *dev,
struct tc_cls_matchall_offload *cls)
{
struct netlink_ext_ack *extack = cls->common.extack;
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct flow_action_entry *act;
int ret;
u32 i;
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
flow_action_for_each(i, act, &cls->rule->action) {
if (act->id == FLOW_ACTION_MIRRED) {
ret = cxgb4_port_mirror_alloc(dev);
if (ret) {
NL_SET_ERR_MSG_MOD(extack,
"Couldn't allocate mirror");
return ret;
}
tc_port_matchall->ingress.viid_mirror = pi->viid_mirror;
break;
}
}
return 0;
}
static void cxgb4_matchall_mirror_free(struct net_device *dev)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
if (!tc_port_matchall->ingress.viid_mirror)
return;
cxgb4_port_mirror_free(dev);
tc_port_matchall->ingress.viid_mirror = 0;
}
static int cxgb4_matchall_del_filter(struct net_device *dev, u8 filter_type)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
int ret;
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
ret = cxgb4_del_filter(dev, tc_port_matchall->ingress.tid[filter_type],
&tc_port_matchall->ingress.fs[filter_type]);
if (ret)
return ret;
tc_port_matchall->ingress.tid[filter_type] = 0;
return 0;
}
static int cxgb4_matchall_add_filter(struct net_device *dev,
struct tc_cls_matchall_offload *cls,
u8 filter_type)
{
struct netlink_ext_ack *extack = cls->common.extack;
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct ch_filter_specification *fs;
int ret, fidx;
/* Get a free filter entry TID, where we can insert this new
* rule. Only insert rule if its prio doesn't conflict with
* existing rules.
*/
fidx = cxgb4_get_free_ftid(dev, filter_type ? PF_INET6 : PF_INET,
false, cls->common.prio);
if (fidx < 0) {
NL_SET_ERR_MSG_MOD(extack,
"No free LETCAM index available");
return -ENOMEM;
}
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
fs = &tc_port_matchall->ingress.fs[filter_type];
memset(fs, 0, sizeof(*fs));
if (fidx < adap->tids.nhpftids)
fs->prio = 1;
fs->tc_prio = cls->common.prio;
fs->tc_cookie = cls->cookie;
fs->type = filter_type;
fs->hitcnts = 1;
fs->val.pfvf_vld = 1;
fs->val.pf = adap->pf;
fs->val.vf = pi->vin;
cxgb4_process_flow_actions(dev, &cls->rule->action, fs);
ret = cxgb4_set_filter(dev, fidx, fs);
if (ret)
return ret;
tc_port_matchall->ingress.tid[filter_type] = fidx;
return 0;
}
static int cxgb4_matchall_alloc_filter(struct net_device *dev,
struct tc_cls_matchall_offload *cls)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
int ret, i;
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
ret = cxgb4_matchall_mirror_alloc(dev, cls);
if (ret)
return ret;
for (i = 0; i < CXGB4_FILTER_TYPE_MAX; i++) {
ret = cxgb4_matchall_add_filter(dev, cls, i);
if (ret)
goto out_free;
}
tc_port_matchall->ingress.state = CXGB4_MATCHALL_STATE_ENABLED;
return 0;
out_free:
while (i-- > 0)
cxgb4_matchall_del_filter(dev, i);
cxgb4_matchall_mirror_free(dev);
return ret;
}
static int cxgb4_matchall_free_filter(struct net_device *dev)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
int ret;
u8 i;
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
for (i = 0; i < CXGB4_FILTER_TYPE_MAX; i++) {
ret = cxgb4_matchall_del_filter(dev, i);
if (ret)
return ret;
}
cxgb4_matchall_mirror_free(dev);
tc_port_matchall->ingress.packets = 0;
tc_port_matchall->ingress.bytes = 0;
tc_port_matchall->ingress.last_used = 0;
tc_port_matchall->ingress.state = CXGB4_MATCHALL_STATE_DISABLED;
return 0;
}
int cxgb4_tc_matchall_replace(struct net_device *dev,
struct tc_cls_matchall_offload *cls_matchall,
bool ingress)
{
struct netlink_ext_ack *extack = cls_matchall->common.extack;
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
int ret;
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
if (ingress) {
if (tc_port_matchall->ingress.state ==
CXGB4_MATCHALL_STATE_ENABLED) {
NL_SET_ERR_MSG_MOD(extack,
"Only 1 Ingress MATCHALL can be offloaded");
return -ENOMEM;
}
ret = cxgb4_validate_flow_actions(dev,
&cls_matchall->rule->action,
extack, 1);
if (ret)
return ret;
return cxgb4_matchall_alloc_filter(dev, cls_matchall);
}
if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) {
NL_SET_ERR_MSG_MOD(extack,
"Only 1 Egress MATCHALL can be offloaded");
return -ENOMEM;
}
ret = cxgb4_matchall_egress_validate(dev, cls_matchall);
if (ret)
return ret;
return cxgb4_matchall_alloc_tc(dev, cls_matchall);
}
int cxgb4_tc_matchall_destroy(struct net_device *dev,
struct tc_cls_matchall_offload *cls_matchall,
bool ingress)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
if (ingress) {
/* All the filter types of this matchall rule save the
* same cookie. So, checking for the first one is
* enough.
*/
if (cls_matchall->cookie !=
tc_port_matchall->ingress.fs[0].tc_cookie)
return -ENOENT;
return cxgb4_matchall_free_filter(dev);
}
if (cls_matchall->cookie != tc_port_matchall->egress.cookie)
return -ENOENT;
cxgb4_matchall_free_tc(dev);
return 0;
}
int cxgb4_tc_matchall_stats(struct net_device *dev,
struct tc_cls_matchall_offload *cls_matchall)
{
u64 tmp_packets, tmp_bytes, packets = 0, bytes = 0;
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct cxgb4_matchall_ingress_entry *ingress;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
int ret;
u8 i;
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
if (tc_port_matchall->ingress.state == CXGB4_MATCHALL_STATE_DISABLED)
return -ENOENT;
ingress = &tc_port_matchall->ingress;
for (i = 0; i < CXGB4_FILTER_TYPE_MAX; i++) {
ret = cxgb4_get_filter_counters(dev, ingress->tid[i],
&tmp_packets, &tmp_bytes,
ingress->fs[i].hash);
if (ret)
return ret;
packets += tmp_packets;
bytes += tmp_bytes;
}
if (tc_port_matchall->ingress.packets != packets) {
flow_stats_update(&cls_matchall->stats,
bytes - tc_port_matchall->ingress.bytes,
packets - tc_port_matchall->ingress.packets,
0, tc_port_matchall->ingress.last_used,
FLOW_ACTION_HW_STATS_IMMEDIATE);
tc_port_matchall->ingress.packets = packets;
tc_port_matchall->ingress.bytes = bytes;
tc_port_matchall->ingress.last_used = jiffies;
}
return 0;
}
static void cxgb4_matchall_disable_offload(struct net_device *dev)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED)
cxgb4_matchall_free_tc(dev);
if (tc_port_matchall->ingress.state == CXGB4_MATCHALL_STATE_ENABLED)
cxgb4_matchall_free_filter(dev);
}
int cxgb4_init_tc_matchall(struct adapter *adap)
{
struct cxgb4_tc_port_matchall *tc_port_matchall;
struct cxgb4_tc_matchall *tc_matchall;
int ret;
tc_matchall = kzalloc_obj(*tc_matchall);
if (!tc_matchall)
return -ENOMEM;
tc_port_matchall = kzalloc_objs(*tc_port_matchall, adap->params.nports);
if (!tc_port_matchall) {
ret = -ENOMEM;
goto out_free_matchall;
}
tc_matchall->port_matchall = tc_port_matchall;
adap->tc_matchall = tc_matchall;
return 0;
out_free_matchall:
kfree(tc_matchall);
return ret;
}
void cxgb4_cleanup_tc_matchall(struct adapter *adap)
{
u8 i;
if (adap->tc_matchall) {
if (adap->tc_matchall->port_matchall) {
for (i = 0; i < adap->params.nports; i++) {
struct net_device *dev = adap->port[i];
if (dev)
cxgb4_matchall_disable_offload(dev);
}
kfree(adap->tc_matchall->port_matchall);
}
kfree(adap->tc_matchall);
}
}
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2019 Chelsio Communications. All rights reserved. */
#ifndef __CXGB4_TC_MATCHALL_H__
#define __CXGB4_TC_MATCHALL_H__
#include <net/pkt_cls.h>
enum cxgb4_matchall_state {
CXGB4_MATCHALL_STATE_DISABLED = 0,
CXGB4_MATCHALL_STATE_ENABLED,
};
struct cxgb4_matchall_egress_entry {
enum cxgb4_matchall_state state; /* Current MATCHALL offload state */
u8 hwtc; /* Traffic class bound to port */
u64 cookie; /* Used to identify the MATCHALL rule offloaded */
};
struct cxgb4_matchall_ingress_entry {
enum cxgb4_matchall_state state; /* Current MATCHALL offload state */
u32 tid[CXGB4_FILTER_TYPE_MAX]; /* Index to hardware filter entries */
/* Filter entries */
struct ch_filter_specification fs[CXGB4_FILTER_TYPE_MAX];
u16 viid_mirror; /* Identifier for allocated Mirror VI */
u64 bytes; /* # of bytes hitting the filter */
u64 packets; /* # of packets hitting the filter */
u64 last_used; /* Last updated jiffies time */
};
struct cxgb4_tc_port_matchall {
struct cxgb4_matchall_egress_entry egress; /* Egress offload info */
struct cxgb4_matchall_ingress_entry ingress; /* Ingress offload info */
};
struct cxgb4_tc_matchall {
struct cxgb4_tc_port_matchall *port_matchall; /* Per port entry */
};
int cxgb4_tc_matchall_replace(struct net_device *dev,
struct tc_cls_matchall_offload *cls_matchall,
bool ingress);
int cxgb4_tc_matchall_destroy(struct net_device *dev,
struct tc_cls_matchall_offload *cls_matchall,
bool ingress);
int cxgb4_tc_matchall_stats(struct net_device *dev,
struct tc_cls_matchall_offload *cls_matchall);
int cxgb4_init_tc_matchall(struct adapter *adap);
void cxgb4_cleanup_tc_matchall(struct adapter *adap);
#endif /* __CXGB4_TC_MATCHALL_H__ */
@@ -0,0 +1,717 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2019 Chelsio Communications. All rights reserved. */
#include "cxgb4.h"
#include "cxgb4_tc_mqprio.h"
#include "sched.h"
static int cxgb4_mqprio_validate(struct net_device *dev,
struct tc_mqprio_qopt_offload *mqprio)
{
u64 min_rate = 0, max_rate = 0, max_link_rate;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
u32 speed, qcount = 0, qoffset = 0;
u32 start_a, start_b, end_a, end_b;
int ret;
u8 i, j;
if (!mqprio->qopt.num_tc)
return 0;
if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS) {
netdev_err(dev, "Only full TC hardware offload is supported\n");
return -EINVAL;
} else if (mqprio->mode != TC_MQPRIO_MODE_CHANNEL) {
netdev_err(dev, "Only channel mode offload is supported\n");
return -EINVAL;
} else if (mqprio->shaper != TC_MQPRIO_SHAPER_BW_RATE) {
netdev_err(dev, "Only bandwidth rate shaper supported\n");
return -EINVAL;
} else if (mqprio->qopt.num_tc > adap->params.nsched_cls) {
netdev_err(dev,
"Only %u traffic classes supported by hardware\n",
adap->params.nsched_cls);
return -ERANGE;
}
ret = t4_get_link_params(pi, NULL, &speed, NULL);
if (ret) {
netdev_err(dev, "Failed to get link speed, ret: %d\n", ret);
return -EINVAL;
}
/* Convert from Mbps to bps */
max_link_rate = (u64)speed * 1000 * 1000;
for (i = 0; i < mqprio->qopt.num_tc; i++) {
qoffset = max_t(u16, mqprio->qopt.offset[i], qoffset);
qcount += mqprio->qopt.count[i];
start_a = mqprio->qopt.offset[i];
end_a = start_a + mqprio->qopt.count[i] - 1;
for (j = i + 1; j < mqprio->qopt.num_tc; j++) {
start_b = mqprio->qopt.offset[j];
end_b = start_b + mqprio->qopt.count[j] - 1;
/* If queue count is 0, then the traffic
* belonging to this class will not use
* ETHOFLD queues. So, no need to validate
* further.
*/
if (!mqprio->qopt.count[i])
break;
if (!mqprio->qopt.count[j])
continue;
if (max_t(u32, start_a, start_b) <=
min_t(u32, end_a, end_b)) {
netdev_err(dev,
"Queues can't overlap across tc\n");
return -EINVAL;
}
}
/* Convert byte per second to bits per second */
min_rate += (mqprio->min_rate[i] * 8);
max_rate += (mqprio->max_rate[i] * 8);
}
if (qoffset >= adap->tids.neotids || qcount > adap->tids.neotids)
return -ENOMEM;
if (min_rate > max_link_rate || max_rate > max_link_rate) {
netdev_err(dev,
"Total Min/Max (%llu/%llu) Rate > supported (%llu)\n",
min_rate, max_rate, max_link_rate);
return -EINVAL;
}
return 0;
}
static int cxgb4_init_eosw_txq(struct net_device *dev,
struct sge_eosw_txq *eosw_txq,
u32 eotid, u32 hwqid)
{
struct adapter *adap = netdev2adap(dev);
struct tx_sw_desc *ring;
memset(eosw_txq, 0, sizeof(*eosw_txq));
ring = kzalloc_objs(*ring, CXGB4_EOSW_TXQ_DEFAULT_DESC_NUM);
if (!ring)
return -ENOMEM;
eosw_txq->desc = ring;
eosw_txq->ndesc = CXGB4_EOSW_TXQ_DEFAULT_DESC_NUM;
spin_lock_init(&eosw_txq->lock);
eosw_txq->state = CXGB4_EO_STATE_CLOSED;
eosw_txq->eotid = eotid;
eosw_txq->hwtid = adap->tids.eotid_base + eosw_txq->eotid;
eosw_txq->cred = adap->params.ofldq_wr_cred;
eosw_txq->hwqid = hwqid;
eosw_txq->netdev = dev;
tasklet_setup(&eosw_txq->qresume_tsk, cxgb4_ethofld_restart);
return 0;
}
static void cxgb4_clean_eosw_txq(struct net_device *dev,
struct sge_eosw_txq *eosw_txq)
{
struct adapter *adap = netdev2adap(dev);
cxgb4_eosw_txq_free_desc(adap, eosw_txq, eosw_txq->ndesc);
eosw_txq->pidx = 0;
eosw_txq->last_pidx = 0;
eosw_txq->cidx = 0;
eosw_txq->last_cidx = 0;
eosw_txq->flowc_idx = 0;
eosw_txq->inuse = 0;
eosw_txq->cred = adap->params.ofldq_wr_cred;
eosw_txq->ncompl = 0;
eosw_txq->last_compl = 0;
eosw_txq->state = CXGB4_EO_STATE_CLOSED;
}
static void cxgb4_free_eosw_txq(struct net_device *dev,
struct sge_eosw_txq *eosw_txq)
{
spin_lock_bh(&eosw_txq->lock);
cxgb4_clean_eosw_txq(dev, eosw_txq);
kfree(eosw_txq->desc);
spin_unlock_bh(&eosw_txq->lock);
tasklet_kill(&eosw_txq->qresume_tsk);
}
static int cxgb4_mqprio_alloc_hw_resources(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct sge_ofld_rxq *eorxq;
struct sge_eohw_txq *eotxq;
int ret, msix = 0;
u32 i;
/* Allocate ETHOFLD hardware queue structures if not done already */
if (!refcount_read(&adap->tc_mqprio->refcnt)) {
adap->sge.eohw_rxq = kzalloc_objs(struct sge_ofld_rxq,
adap->sge.eoqsets);
if (!adap->sge.eohw_rxq)
return -ENOMEM;
adap->sge.eohw_txq = kzalloc_objs(struct sge_eohw_txq,
adap->sge.eoqsets);
if (!adap->sge.eohw_txq) {
kfree(adap->sge.eohw_rxq);
return -ENOMEM;
}
refcount_set(&adap->tc_mqprio->refcnt, 1);
} else {
refcount_inc(&adap->tc_mqprio->refcnt);
}
if (!(adap->flags & CXGB4_USING_MSIX))
msix = -((int)adap->sge.intrq.abs_id + 1);
for (i = 0; i < pi->nqsets; i++) {
eorxq = &adap->sge.eohw_rxq[pi->first_qset + i];
eotxq = &adap->sge.eohw_txq[pi->first_qset + i];
/* Allocate Rxqs for receiving ETHOFLD Tx completions */
if (msix >= 0) {
msix = cxgb4_get_msix_idx_from_bmap(adap);
if (msix < 0) {
ret = msix;
goto out_free_queues;
}
eorxq->msix = &adap->msix_info[msix];
snprintf(eorxq->msix->desc,
sizeof(eorxq->msix->desc),
"%s-eorxq%d", dev->name, i);
}
init_rspq(adap, &eorxq->rspq,
CXGB4_EOHW_RXQ_DEFAULT_INTR_USEC,
CXGB4_EOHW_RXQ_DEFAULT_PKT_CNT,
CXGB4_EOHW_RXQ_DEFAULT_DESC_NUM,
CXGB4_EOHW_RXQ_DEFAULT_DESC_SIZE);
eorxq->fl.size = CXGB4_EOHW_FLQ_DEFAULT_DESC_NUM;
ret = t4_sge_alloc_rxq(adap, &eorxq->rspq, false,
dev, msix, &eorxq->fl,
cxgb4_ethofld_rx_handler,
NULL, 0);
if (ret)
goto out_free_queues;
/* Allocate ETHOFLD hardware Txqs */
eotxq->q.size = CXGB4_EOHW_TXQ_DEFAULT_DESC_NUM;
ret = t4_sge_alloc_ethofld_txq(adap, eotxq, dev,
eorxq->rspq.cntxt_id);
if (ret)
goto out_free_queues;
/* Allocate IRQs, set IRQ affinity, and start Rx */
if (adap->flags & CXGB4_USING_MSIX) {
ret = request_irq(eorxq->msix->vec, t4_sge_intr_msix, 0,
eorxq->msix->desc, &eorxq->rspq);
if (ret)
goto out_free_msix;
cxgb4_set_msix_aff(adap, eorxq->msix->vec,
&eorxq->msix->aff_mask, i);
}
if (adap->flags & CXGB4_FULL_INIT_DONE)
cxgb4_enable_rx(adap, &eorxq->rspq);
}
return 0;
out_free_msix:
while (i-- > 0) {
eorxq = &adap->sge.eohw_rxq[pi->first_qset + i];
if (adap->flags & CXGB4_FULL_INIT_DONE)
cxgb4_quiesce_rx(&eorxq->rspq);
if (adap->flags & CXGB4_USING_MSIX) {
cxgb4_clear_msix_aff(eorxq->msix->vec,
eorxq->msix->aff_mask);
free_irq(eorxq->msix->vec, &eorxq->rspq);
}
}
out_free_queues:
for (i = 0; i < pi->nqsets; i++) {
eorxq = &adap->sge.eohw_rxq[pi->first_qset + i];
eotxq = &adap->sge.eohw_txq[pi->first_qset + i];
if (eorxq->rspq.desc)
free_rspq_fl(adap, &eorxq->rspq, &eorxq->fl);
if (eorxq->msix)
cxgb4_free_msix_idx_in_bmap(adap, eorxq->msix->idx);
t4_sge_free_ethofld_txq(adap, eotxq);
}
if (refcount_dec_and_test(&adap->tc_mqprio->refcnt)) {
kfree(adap->sge.eohw_txq);
kfree(adap->sge.eohw_rxq);
}
return ret;
}
static void cxgb4_mqprio_free_hw_resources(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct sge_ofld_rxq *eorxq;
struct sge_eohw_txq *eotxq;
u32 i;
/* Return if no ETHOFLD structures have been allocated yet */
if (!refcount_read(&adap->tc_mqprio->refcnt))
return;
/* Return if no hardware queues have been allocated */
if (!adap->sge.eohw_rxq[pi->first_qset].rspq.desc)
return;
for (i = 0; i < pi->nqsets; i++) {
eorxq = &adap->sge.eohw_rxq[pi->first_qset + i];
eotxq = &adap->sge.eohw_txq[pi->first_qset + i];
/* Device removal path will already disable NAPI
* before unregistering netdevice. So, only disable
* NAPI if we're not in device removal path
*/
if (!(adap->flags & CXGB4_SHUTTING_DOWN))
cxgb4_quiesce_rx(&eorxq->rspq);
if (adap->flags & CXGB4_USING_MSIX) {
cxgb4_clear_msix_aff(eorxq->msix->vec,
eorxq->msix->aff_mask);
free_irq(eorxq->msix->vec, &eorxq->rspq);
cxgb4_free_msix_idx_in_bmap(adap, eorxq->msix->idx);
}
free_rspq_fl(adap, &eorxq->rspq, &eorxq->fl);
t4_sge_free_ethofld_txq(adap, eotxq);
}
/* Free up ETHOFLD structures if there are no users */
if (refcount_dec_and_test(&adap->tc_mqprio->refcnt)) {
kfree(adap->sge.eohw_txq);
kfree(adap->sge.eohw_rxq);
}
}
static int cxgb4_mqprio_alloc_tc(struct net_device *dev,
struct tc_mqprio_qopt_offload *mqprio)
{
struct ch_sched_params p = {
.type = SCHED_CLASS_TYPE_PACKET,
.u.params.level = SCHED_CLASS_LEVEL_CL_RL,
.u.params.mode = SCHED_CLASS_MODE_FLOW,
.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS,
.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS,
.u.params.class = SCHED_CLS_NONE,
.u.params.weight = 0,
.u.params.pktsize = dev->mtu,
};
struct cxgb4_tc_port_mqprio *tc_port_mqprio;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct ch_sched_class *e;
int ret;
u8 i;
tc_port_mqprio = &adap->tc_mqprio->port_mqprio[pi->port_id];
p.u.params.channel = pi->tx_chan;
for (i = 0; i < mqprio->qopt.num_tc; i++) {
/* Convert from bytes per second to Kbps */
p.u.params.minrate = div_u64(mqprio->min_rate[i] * 8, 1000);
p.u.params.maxrate = div_u64(mqprio->max_rate[i] * 8, 1000);
/* Request larger burst buffer for smaller MTU, so
* that hardware can work on more data per burst
* cycle.
*/
if (dev->mtu <= ETH_DATA_LEN)
p.u.params.burstsize = 8 * dev->mtu;
e = cxgb4_sched_class_alloc(dev, &p);
if (!e) {
ret = -ENOMEM;
goto out_err;
}
tc_port_mqprio->tc_hwtc_map[i] = e->idx;
}
return 0;
out_err:
while (i--)
cxgb4_sched_class_free(dev, tc_port_mqprio->tc_hwtc_map[i]);
return ret;
}
static void cxgb4_mqprio_free_tc(struct net_device *dev)
{
struct cxgb4_tc_port_mqprio *tc_port_mqprio;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
u8 i;
tc_port_mqprio = &adap->tc_mqprio->port_mqprio[pi->port_id];
for (i = 0; i < tc_port_mqprio->mqprio.qopt.num_tc; i++)
cxgb4_sched_class_free(dev, tc_port_mqprio->tc_hwtc_map[i]);
}
static int cxgb4_mqprio_class_bind(struct net_device *dev,
struct sge_eosw_txq *eosw_txq,
u8 tc)
{
struct ch_sched_flowc fe;
int ret;
init_completion(&eosw_txq->completion);
fe.tid = eosw_txq->eotid;
fe.class = tc;
ret = cxgb4_sched_class_bind(dev, &fe, SCHED_FLOWC);
if (ret)
return ret;
ret = wait_for_completion_timeout(&eosw_txq->completion,
CXGB4_FLOWC_WAIT_TIMEOUT);
if (!ret)
return -ETIMEDOUT;
return 0;
}
static void cxgb4_mqprio_class_unbind(struct net_device *dev,
struct sge_eosw_txq *eosw_txq,
u8 tc)
{
struct adapter *adap = netdev2adap(dev);
struct ch_sched_flowc fe;
/* If we're shutting down, interrupts are disabled and no completions
* come back. So, skip waiting for completions in this scenario.
*/
if (!(adap->flags & CXGB4_SHUTTING_DOWN))
init_completion(&eosw_txq->completion);
fe.tid = eosw_txq->eotid;
fe.class = tc;
cxgb4_sched_class_unbind(dev, &fe, SCHED_FLOWC);
if (!(adap->flags & CXGB4_SHUTTING_DOWN))
wait_for_completion_timeout(&eosw_txq->completion,
CXGB4_FLOWC_WAIT_TIMEOUT);
}
static int cxgb4_mqprio_enable_offload(struct net_device *dev,
struct tc_mqprio_qopt_offload *mqprio)
{
struct cxgb4_tc_port_mqprio *tc_port_mqprio;
u32 qoffset, qcount, tot_qcount, qid, hwqid;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct sge_eosw_txq *eosw_txq;
int eotid, ret;
u16 i, j;
u8 hwtc;
ret = cxgb4_mqprio_alloc_hw_resources(dev);
if (ret)
return -ENOMEM;
tc_port_mqprio = &adap->tc_mqprio->port_mqprio[pi->port_id];
for (i = 0; i < mqprio->qopt.num_tc; i++) {
qoffset = mqprio->qopt.offset[i];
qcount = mqprio->qopt.count[i];
for (j = 0; j < qcount; j++) {
eotid = cxgb4_get_free_eotid(&adap->tids);
if (eotid < 0) {
ret = -ENOMEM;
goto out_free_eotids;
}
qid = qoffset + j;
hwqid = pi->first_qset + (eotid % pi->nqsets);
eosw_txq = &tc_port_mqprio->eosw_txq[qid];
ret = cxgb4_init_eosw_txq(dev, eosw_txq,
eotid, hwqid);
if (ret)
goto out_free_eotids;
cxgb4_alloc_eotid(&adap->tids, eotid, eosw_txq);
hwtc = tc_port_mqprio->tc_hwtc_map[i];
ret = cxgb4_mqprio_class_bind(dev, eosw_txq, hwtc);
if (ret)
goto out_free_eotids;
}
}
memcpy(&tc_port_mqprio->mqprio, mqprio,
sizeof(struct tc_mqprio_qopt_offload));
/* Inform the stack about the configured tc params.
*
* Set the correct queue map. If no queue count has been
* specified, then send the traffic through default NIC
* queues; instead of ETHOFLD queues.
*/
ret = netdev_set_num_tc(dev, mqprio->qopt.num_tc);
if (ret)
goto out_free_eotids;
tot_qcount = pi->nqsets;
for (i = 0; i < mqprio->qopt.num_tc; i++) {
qcount = mqprio->qopt.count[i];
if (qcount) {
qoffset = mqprio->qopt.offset[i] + pi->nqsets;
} else {
qcount = pi->nqsets;
qoffset = 0;
}
ret = netdev_set_tc_queue(dev, i, qcount, qoffset);
if (ret)
goto out_reset_tc;
tot_qcount += mqprio->qopt.count[i];
}
ret = netif_set_real_num_tx_queues(dev, tot_qcount);
if (ret)
goto out_reset_tc;
tc_port_mqprio->state = CXGB4_MQPRIO_STATE_ACTIVE;
return 0;
out_reset_tc:
netdev_reset_tc(dev);
i = mqprio->qopt.num_tc;
out_free_eotids:
while (i-- > 0) {
qoffset = mqprio->qopt.offset[i];
qcount = mqprio->qopt.count[i];
for (j = 0; j < qcount; j++) {
eosw_txq = &tc_port_mqprio->eosw_txq[qoffset + j];
hwtc = tc_port_mqprio->tc_hwtc_map[i];
cxgb4_mqprio_class_unbind(dev, eosw_txq, hwtc);
cxgb4_free_eotid(&adap->tids, eosw_txq->eotid);
cxgb4_free_eosw_txq(dev, eosw_txq);
}
}
cxgb4_mqprio_free_hw_resources(dev);
return ret;
}
static void cxgb4_mqprio_disable_offload(struct net_device *dev)
{
struct cxgb4_tc_port_mqprio *tc_port_mqprio;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct sge_eosw_txq *eosw_txq;
u32 qoffset, qcount;
u16 i, j;
u8 hwtc;
tc_port_mqprio = &adap->tc_mqprio->port_mqprio[pi->port_id];
if (tc_port_mqprio->state != CXGB4_MQPRIO_STATE_ACTIVE)
return;
netdev_reset_tc(dev);
netif_set_real_num_tx_queues(dev, pi->nqsets);
for (i = 0; i < tc_port_mqprio->mqprio.qopt.num_tc; i++) {
qoffset = tc_port_mqprio->mqprio.qopt.offset[i];
qcount = tc_port_mqprio->mqprio.qopt.count[i];
for (j = 0; j < qcount; j++) {
eosw_txq = &tc_port_mqprio->eosw_txq[qoffset + j];
hwtc = tc_port_mqprio->tc_hwtc_map[i];
cxgb4_mqprio_class_unbind(dev, eosw_txq, hwtc);
cxgb4_free_eotid(&adap->tids, eosw_txq->eotid);
cxgb4_free_eosw_txq(dev, eosw_txq);
}
}
cxgb4_mqprio_free_hw_resources(dev);
/* Free up the traffic classes */
cxgb4_mqprio_free_tc(dev);
memset(&tc_port_mqprio->mqprio, 0,
sizeof(struct tc_mqprio_qopt_offload));
tc_port_mqprio->state = CXGB4_MQPRIO_STATE_DISABLED;
}
int cxgb4_setup_tc_mqprio(struct net_device *dev,
struct tc_mqprio_qopt_offload *mqprio)
{
struct adapter *adap = netdev2adap(dev);
bool needs_bring_up = false;
int ret;
ret = cxgb4_mqprio_validate(dev, mqprio);
if (ret)
return ret;
mutex_lock(&adap->tc_mqprio->mqprio_mutex);
/* To configure tc params, the current allocated EOTIDs must
* be freed up. However, they can't be freed up if there's
* traffic running on the interface. So, ensure interface is
* down before configuring tc params.
*/
if (netif_running(dev)) {
netif_tx_stop_all_queues(dev);
netif_carrier_off(dev);
needs_bring_up = true;
}
cxgb4_mqprio_disable_offload(dev);
/* If requested for clear, then just return since resources are
* already freed up by now.
*/
if (!mqprio->qopt.num_tc)
goto out;
/* Allocate free available traffic classes and configure
* their rate parameters.
*/
ret = cxgb4_mqprio_alloc_tc(dev, mqprio);
if (ret)
goto out;
ret = cxgb4_mqprio_enable_offload(dev, mqprio);
if (ret) {
cxgb4_mqprio_free_tc(dev);
goto out;
}
out:
if (needs_bring_up) {
netif_tx_start_all_queues(dev);
netif_carrier_on(dev);
}
mutex_unlock(&adap->tc_mqprio->mqprio_mutex);
return ret;
}
void cxgb4_mqprio_stop_offload(struct adapter *adap)
{
struct cxgb4_tc_port_mqprio *tc_port_mqprio;
struct net_device *dev;
u8 i;
if (!adap->tc_mqprio || !adap->tc_mqprio->port_mqprio)
return;
mutex_lock(&adap->tc_mqprio->mqprio_mutex);
for_each_port(adap, i) {
dev = adap->port[i];
if (!dev)
continue;
tc_port_mqprio = &adap->tc_mqprio->port_mqprio[i];
if (!tc_port_mqprio->mqprio.qopt.num_tc)
continue;
cxgb4_mqprio_disable_offload(dev);
}
mutex_unlock(&adap->tc_mqprio->mqprio_mutex);
}
int cxgb4_init_tc_mqprio(struct adapter *adap)
{
struct cxgb4_tc_port_mqprio *tc_port_mqprio, *port_mqprio;
struct cxgb4_tc_mqprio *tc_mqprio;
struct sge_eosw_txq *eosw_txq;
int ret = 0;
u8 i;
tc_mqprio = kzalloc_obj(*tc_mqprio);
if (!tc_mqprio)
return -ENOMEM;
tc_port_mqprio = kzalloc_objs(*tc_port_mqprio, adap->params.nports);
if (!tc_port_mqprio) {
ret = -ENOMEM;
goto out_free_mqprio;
}
mutex_init(&tc_mqprio->mqprio_mutex);
tc_mqprio->port_mqprio = tc_port_mqprio;
for (i = 0; i < adap->params.nports; i++) {
port_mqprio = &tc_mqprio->port_mqprio[i];
eosw_txq = kzalloc_objs(*eosw_txq, adap->tids.neotids);
if (!eosw_txq) {
ret = -ENOMEM;
goto out_free_ports;
}
port_mqprio->eosw_txq = eosw_txq;
}
adap->tc_mqprio = tc_mqprio;
refcount_set(&adap->tc_mqprio->refcnt, 0);
return 0;
out_free_ports:
for (i = 0; i < adap->params.nports; i++) {
port_mqprio = &tc_mqprio->port_mqprio[i];
kfree(port_mqprio->eosw_txq);
}
kfree(tc_port_mqprio);
out_free_mqprio:
kfree(tc_mqprio);
return ret;
}
void cxgb4_cleanup_tc_mqprio(struct adapter *adap)
{
struct cxgb4_tc_port_mqprio *port_mqprio;
u8 i;
if (adap->tc_mqprio) {
mutex_lock(&adap->tc_mqprio->mqprio_mutex);
if (adap->tc_mqprio->port_mqprio) {
for (i = 0; i < adap->params.nports; i++) {
struct net_device *dev = adap->port[i];
if (dev)
cxgb4_mqprio_disable_offload(dev);
port_mqprio = &adap->tc_mqprio->port_mqprio[i];
kfree(port_mqprio->eosw_txq);
}
kfree(adap->tc_mqprio->port_mqprio);
}
mutex_unlock(&adap->tc_mqprio->mqprio_mutex);
kfree(adap->tc_mqprio);
}
}
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2019 Chelsio Communications. All rights reserved. */
#ifndef __CXGB4_TC_MQPRIO_H__
#define __CXGB4_TC_MQPRIO_H__
#include <net/pkt_sched.h>
#define CXGB4_EOSW_TXQ_DEFAULT_DESC_NUM 128
#define CXGB4_EOHW_TXQ_DEFAULT_DESC_NUM 1024
#define CXGB4_EOHW_RXQ_DEFAULT_DESC_NUM 1024
#define CXGB4_EOHW_RXQ_DEFAULT_DESC_SIZE 64
#define CXGB4_EOHW_RXQ_DEFAULT_INTR_USEC 5
#define CXGB4_EOHW_RXQ_DEFAULT_PKT_CNT 8
#define CXGB4_EOHW_FLQ_DEFAULT_DESC_NUM 72
#define CXGB4_FLOWC_WAIT_TIMEOUT (5 * HZ)
enum cxgb4_mqprio_state {
CXGB4_MQPRIO_STATE_DISABLED = 0,
CXGB4_MQPRIO_STATE_ACTIVE,
};
struct cxgb4_tc_port_mqprio {
enum cxgb4_mqprio_state state; /* Current MQPRIO offload state */
struct tc_mqprio_qopt_offload mqprio; /* MQPRIO offload params */
struct sge_eosw_txq *eosw_txq; /* Netdev SW Tx queue array */
u8 tc_hwtc_map[TC_QOPT_MAX_QUEUE]; /* MQPRIO tc to hardware tc map */
};
struct cxgb4_tc_mqprio {
refcount_t refcnt; /* Refcount for adapter-wide resources */
struct mutex mqprio_mutex; /* Lock for accessing MQPRIO info */
struct cxgb4_tc_port_mqprio *port_mqprio; /* Per port MQPRIO info */
};
int cxgb4_setup_tc_mqprio(struct net_device *dev,
struct tc_mqprio_qopt_offload *mqprio);
void cxgb4_mqprio_stop_offload(struct adapter *adap);
int cxgb4_init_tc_mqprio(struct adapter *adap);
void cxgb4_cleanup_tc_mqprio(struct adapter *adap);
#endif /* __CXGB4_TC_MQPRIO_H__ */
@@ -0,0 +1,532 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <net/tc_act/tc_gact.h>
#include <net/tc_act/tc_mirred.h>
#include "cxgb4.h"
#include "cxgb4_filter.h"
#include "cxgb4_tc_u32_parse.h"
#include "cxgb4_tc_u32.h"
/* Fill ch_filter_specification with parsed match value/mask pair. */
static int fill_match_fields(struct adapter *adap,
struct ch_filter_specification *fs,
struct tc_cls_u32_offload *cls,
const struct cxgb4_match_field *entry,
bool next_header)
{
unsigned int i, j;
__be32 val, mask;
int off, err;
bool found;
for (i = 0; i < cls->knode.sel->nkeys; i++) {
off = cls->knode.sel->keys[i].off;
val = cls->knode.sel->keys[i].val;
mask = cls->knode.sel->keys[i].mask;
if (next_header) {
/* For next headers, parse only keys with offmask */
if (!cls->knode.sel->keys[i].offmask)
continue;
} else {
/* For the remaining, parse only keys without offmask */
if (cls->knode.sel->keys[i].offmask)
continue;
}
found = false;
for (j = 0; entry[j].val; j++) {
if (off == entry[j].off) {
found = true;
err = entry[j].val(fs, val, mask);
if (err)
return err;
break;
}
}
if (!found)
return -EINVAL;
}
return 0;
}
/* Fill ch_filter_specification with parsed action. */
static int fill_action_fields(struct adapter *adap,
struct ch_filter_specification *fs,
struct tc_cls_u32_offload *cls)
{
unsigned int num_actions = 0;
const struct tc_action *a;
struct tcf_exts *exts;
int i;
exts = cls->knode.exts;
if (!tcf_exts_has_actions(exts))
return -EINVAL;
tcf_exts_for_each_action(i, a, exts) {
/* Don't allow more than one action per rule. */
if (num_actions)
return -EINVAL;
/* Drop in hardware. */
if (is_tcf_gact_shot(a)) {
fs->action = FILTER_DROP;
num_actions++;
continue;
}
/* Re-direct to specified port in hardware. */
if (is_tcf_mirred_egress_redirect(a)) {
struct net_device *n_dev, *target_dev;
bool found = false;
unsigned int i;
target_dev = tcf_mirred_dev(a);
for_each_port(adap, i) {
n_dev = adap->port[i];
if (target_dev == n_dev) {
fs->action = FILTER_SWITCH;
fs->eport = i;
found = true;
break;
}
}
/* Interface doesn't belong to any port of
* the underlying hardware.
*/
if (!found)
return -EINVAL;
num_actions++;
continue;
}
/* Un-supported action. */
return -EINVAL;
}
return 0;
}
int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
{
const struct cxgb4_match_field *start, *link_start = NULL;
struct netlink_ext_ack *extack = cls->common.extack;
struct adapter *adapter = netdev2adap(dev);
__be16 protocol = cls->common.protocol;
struct ch_filter_specification fs;
struct cxgb4_tc_u32_table *t;
struct cxgb4_link *link;
u32 uhtid, link_uhtid;
bool is_ipv6 = false;
u8 inet_family;
int filter_id;
int ret;
if (!can_tc_u32_offload(dev))
return -EOPNOTSUPP;
if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6))
return -EOPNOTSUPP;
inet_family = (protocol == htons(ETH_P_IPV6)) ? PF_INET6 : PF_INET;
/* Get a free filter entry TID, where we can insert this new
* rule. Only insert rule if its prio doesn't conflict with
* existing rules.
*/
filter_id = cxgb4_get_free_ftid(dev, inet_family, false,
TC_U32_NODE(cls->knode.handle));
if (filter_id < 0) {
NL_SET_ERR_MSG_MOD(extack,
"No free LETCAM index available");
return -ENOMEM;
}
t = adapter->tc_u32;
uhtid = TC_U32_USERHTID(cls->knode.handle);
link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
/* Ensure that uhtid is either root u32 (i.e. 0x800)
* or a valid linked bucket.
*/
if (uhtid != 0x800 && uhtid >= t->size)
return -EINVAL;
/* Ensure link handle uhtid is sane, if specified. */
if (link_uhtid >= t->size)
return -EINVAL;
memset(&fs, 0, sizeof(fs));
if (filter_id < adapter->tids.nhpftids)
fs.prio = 1;
fs.tc_prio = cls->common.prio;
fs.tc_cookie = cls->knode.handle;
if (protocol == htons(ETH_P_IPV6)) {
start = cxgb4_ipv6_fields;
is_ipv6 = true;
} else {
start = cxgb4_ipv4_fields;
is_ipv6 = false;
}
if (uhtid != 0x800) {
/* Link must exist from root node before insertion. */
if (!t->table[uhtid - 1].link_handle)
return -EINVAL;
/* Link must have a valid supported next header. */
link_start = t->table[uhtid - 1].match_field;
if (!link_start)
return -EINVAL;
}
/* Parse links and record them for subsequent jumps to valid
* next headers.
*/
if (link_uhtid) {
const struct cxgb4_next_header *next;
bool found = false;
unsigned int i, j;
__be32 val, mask;
int off;
if (t->table[link_uhtid - 1].link_handle) {
dev_err(adapter->pdev_dev,
"Link handle exists for: 0x%x\n",
link_uhtid);
return -EINVAL;
}
next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
/* Try to find matches that allow jumps to next header. */
for (i = 0; next[i].jump; i++) {
if (next[i].sel.offoff != cls->knode.sel->offoff ||
next[i].sel.offshift != cls->knode.sel->offshift ||
next[i].sel.offmask != cls->knode.sel->offmask ||
next[i].sel.off != cls->knode.sel->off)
continue;
/* Found a possible candidate. Find a key that
* matches the corresponding offset, value, and
* mask to jump to next header.
*/
for (j = 0; j < cls->knode.sel->nkeys; j++) {
off = cls->knode.sel->keys[j].off;
val = cls->knode.sel->keys[j].val;
mask = cls->knode.sel->keys[j].mask;
if (next[i].key.off == off &&
next[i].key.val == val &&
next[i].key.mask == mask) {
found = true;
break;
}
}
if (!found)
continue; /* Try next candidate. */
/* Candidate to jump to next header found.
* Translate all keys to internal specification
* and store them in jump table. This spec is copied
* later to set the actual filters.
*/
ret = fill_match_fields(adapter, &fs, cls,
start, false);
if (ret)
goto out;
link = &t->table[link_uhtid - 1];
link->match_field = next[i].jump;
link->link_handle = cls->knode.handle;
memcpy(&link->fs, &fs, sizeof(fs));
break;
}
/* No candidate found to jump to next header. */
if (!found)
return -EINVAL;
return 0;
}
/* Fill ch_filter_specification match fields to be shipped to hardware.
* Copy the linked spec (if any) first. And then update the spec as
* needed.
*/
if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) {
/* Copy linked ch_filter_specification */
memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
ret = fill_match_fields(adapter, &fs, cls,
link_start, true);
if (ret)
goto out;
}
ret = fill_match_fields(adapter, &fs, cls, start, false);
if (ret)
goto out;
/* Fill ch_filter_specification action fields to be shipped to
* hardware.
*/
ret = fill_action_fields(adapter, &fs, cls);
if (ret)
goto out;
/* The filter spec has been completely built from the info
* provided from u32. We now set some default fields in the
* spec for sanity.
*/
/* Match only packets coming from the ingress port where this
* filter will be created.
*/
fs.val.iport = netdev2pinfo(dev)->port_id;
fs.mask.iport = ~0;
/* Enable filter hit counts. */
fs.hitcnts = 1;
/* Set type of filter - IPv6 or IPv4 */
fs.type = is_ipv6 ? 1 : 0;
/* Set the filter */
ret = cxgb4_set_filter(dev, filter_id, &fs);
if (ret)
goto out;
/* If this is a linked bucket, then set the corresponding
* entry in the bitmap to mark it as belonging to this linked
* bucket.
*/
if (uhtid != 0x800 && t->table[uhtid - 1].link_handle)
set_bit(filter_id, t->table[uhtid - 1].tid_map);
out:
return ret;
}
int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
{
struct adapter *adapter = netdev2adap(dev);
unsigned int filter_id, max_tids, i, j;
struct cxgb4_link *link = NULL;
struct cxgb4_tc_u32_table *t;
struct filter_entry *f;
bool found = false;
u32 handle, uhtid;
u8 nslots;
int ret;
if (!can_tc_u32_offload(dev))
return -EOPNOTSUPP;
/* Fetch the location to delete the filter. */
max_tids = adapter->tids.nhpftids + adapter->tids.nftids;
spin_lock_bh(&adapter->tids.ftid_lock);
filter_id = 0;
while (filter_id < max_tids) {
if (filter_id < adapter->tids.nhpftids) {
i = filter_id;
f = &adapter->tids.hpftid_tab[i];
if (f->valid && f->fs.tc_cookie == cls->knode.handle) {
found = true;
break;
}
i = find_next_bit(adapter->tids.hpftid_bmap,
adapter->tids.nhpftids, i + 1);
if (i >= adapter->tids.nhpftids) {
filter_id = adapter->tids.nhpftids;
continue;
}
filter_id = i;
} else {
i = filter_id - adapter->tids.nhpftids;
f = &adapter->tids.ftid_tab[i];
if (f->valid && f->fs.tc_cookie == cls->knode.handle) {
found = true;
break;
}
i = find_next_bit(adapter->tids.ftid_bmap,
adapter->tids.nftids, i + 1);
if (i >= adapter->tids.nftids)
break;
filter_id = i + adapter->tids.nhpftids;
}
nslots = 0;
if (f->fs.type) {
nslots++;
if (CHELSIO_CHIP_VERSION(adapter->params.chip) <
CHELSIO_T6)
nslots += 2;
}
filter_id += nslots;
}
spin_unlock_bh(&adapter->tids.ftid_lock);
if (!found)
return -ERANGE;
t = adapter->tc_u32;
handle = cls->knode.handle;
uhtid = TC_U32_USERHTID(cls->knode.handle);
/* Ensure that uhtid is either root u32 (i.e. 0x800)
* or a valid linked bucket.
*/
if (uhtid != 0x800 && uhtid >= t->size)
return -EINVAL;
/* Delete the specified filter */
if (uhtid != 0x800) {
link = &t->table[uhtid - 1];
if (!link->link_handle)
return -EINVAL;
if (!test_bit(filter_id, link->tid_map))
return -EINVAL;
}
ret = cxgb4_del_filter(dev, filter_id, NULL);
if (ret)
goto out;
if (link)
clear_bit(filter_id, link->tid_map);
/* If a link is being deleted, then delete all filters
* associated with the link.
*/
for (i = 0; i < t->size; i++) {
link = &t->table[i];
if (link->link_handle == handle) {
for (j = 0; j < max_tids; j++) {
if (!test_bit(j, link->tid_map))
continue;
ret = __cxgb4_del_filter(dev, j, NULL, NULL);
if (ret)
goto out;
clear_bit(j, link->tid_map);
}
/* Clear the link state */
link->match_field = NULL;
link->link_handle = 0;
memset(&link->fs, 0, sizeof(link->fs));
break;
}
}
out:
return ret;
}
void cxgb4_cleanup_tc_u32(struct adapter *adap)
{
struct cxgb4_tc_u32_table *t;
unsigned int i;
if (!adap->tc_u32)
return;
/* Free up all allocated memory. */
t = adap->tc_u32;
for (i = 0; i < t->size; i++) {
struct cxgb4_link *link = &t->table[i];
kvfree(link->tid_map);
}
kvfree(adap->tc_u32);
}
struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap)
{
unsigned int max_tids = adap->tids.nftids + adap->tids.nhpftids;
struct cxgb4_tc_u32_table *t;
unsigned int i;
if (!max_tids)
return NULL;
t = kvzalloc_flex(*t, table, max_tids);
if (!t)
return NULL;
t->size = max_tids;
for (i = 0; i < t->size; i++) {
struct cxgb4_link *link = &t->table[i];
unsigned int bmap_size;
bmap_size = BITS_TO_LONGS(max_tids);
link->tid_map = kvcalloc(bmap_size, sizeof(unsigned long),
GFP_KERNEL);
if (!link->tid_map)
goto out_no_mem;
bitmap_zero(link->tid_map, max_tids);
}
return t;
out_no_mem:
for (i = 0; i < t->size; i++) {
struct cxgb4_link *link = &t->table[i];
kvfree(link->tid_map);
}
kvfree(t);
return NULL;
}
@@ -0,0 +1,52 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_TC_U32_H
#define __CXGB4_TC_U32_H
#include <net/pkt_cls.h>
static inline bool can_tc_u32_offload(struct net_device *dev)
{
struct adapter *adap = netdev2adap(dev);
return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false;
}
int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls);
int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls);
void cxgb4_cleanup_tc_u32(struct adapter *adapter);
struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap);
#endif /* __CXGB4_TC_U32_H */
@@ -0,0 +1,336 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_TC_U32_PARSE_H
#define __CXGB4_TC_U32_PARSE_H
struct cxgb4_match_field {
int off; /* Offset from the beginning of the header to match */
/* Fill the value/mask pair in the spec if matched */
int (*val)(struct ch_filter_specification *f, __be32 val, __be32 mask);
};
/* IPv4 match fields */
static inline int cxgb4_fill_ipv4_tos(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
f->val.tos = (ntohl(val) >> 16) & 0x000000FF;
f->mask.tos = (ntohl(mask) >> 16) & 0x000000FF;
return 0;
}
static inline int cxgb4_fill_ipv4_frag(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
u32 mask_val;
u8 frag_val;
frag_val = (ntohl(val) >> 13) & 0x00000007;
mask_val = ntohl(mask) & 0x0000FFFF;
if (frag_val == 0x1 && mask_val != 0x3FFF) { /* MF set */
f->val.frag = 1;
f->mask.frag = 1;
} else if (frag_val == 0x2 && mask_val != 0x3FFF) { /* DF set */
f->val.frag = 0;
f->mask.frag = 1;
} else {
return -EINVAL;
}
return 0;
}
static inline int cxgb4_fill_ipv4_proto(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
f->val.proto = (ntohl(val) >> 16) & 0x000000FF;
f->mask.proto = (ntohl(mask) >> 16) & 0x000000FF;
return 0;
}
static inline int cxgb4_fill_ipv4_src_ip(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.fip[0], &val, sizeof(u32));
memcpy(&f->mask.fip[0], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv4_dst_ip(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.lip[0], &val, sizeof(u32));
memcpy(&f->mask.lip[0], &mask, sizeof(u32));
return 0;
}
static const struct cxgb4_match_field cxgb4_ipv4_fields[] = {
{ .off = 0, .val = cxgb4_fill_ipv4_tos },
{ .off = 4, .val = cxgb4_fill_ipv4_frag },
{ .off = 8, .val = cxgb4_fill_ipv4_proto },
{ .off = 12, .val = cxgb4_fill_ipv4_src_ip },
{ .off = 16, .val = cxgb4_fill_ipv4_dst_ip },
{ .val = NULL }
};
/* IPv6 match fields */
static inline int cxgb4_fill_ipv6_tos(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
f->val.tos = (ntohl(val) >> 20) & 0x000000FF;
f->mask.tos = (ntohl(mask) >> 20) & 0x000000FF;
return 0;
}
static inline int cxgb4_fill_ipv6_proto(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
f->val.proto = (ntohl(val) >> 8) & 0x000000FF;
f->mask.proto = (ntohl(mask) >> 8) & 0x000000FF;
return 0;
}
static inline int cxgb4_fill_ipv6_src_ip0(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.fip[0], &val, sizeof(u32));
memcpy(&f->mask.fip[0], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv6_src_ip1(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.fip[4], &val, sizeof(u32));
memcpy(&f->mask.fip[4], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv6_src_ip2(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.fip[8], &val, sizeof(u32));
memcpy(&f->mask.fip[8], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv6_src_ip3(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.fip[12], &val, sizeof(u32));
memcpy(&f->mask.fip[12], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv6_dst_ip0(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.lip[0], &val, sizeof(u32));
memcpy(&f->mask.lip[0], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv6_dst_ip1(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.lip[4], &val, sizeof(u32));
memcpy(&f->mask.lip[4], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv6_dst_ip2(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.lip[8], &val, sizeof(u32));
memcpy(&f->mask.lip[8], &mask, sizeof(u32));
return 0;
}
static inline int cxgb4_fill_ipv6_dst_ip3(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
memcpy(&f->val.lip[12], &val, sizeof(u32));
memcpy(&f->mask.lip[12], &mask, sizeof(u32));
return 0;
}
static const struct cxgb4_match_field cxgb4_ipv6_fields[] = {
{ .off = 0, .val = cxgb4_fill_ipv6_tos },
{ .off = 4, .val = cxgb4_fill_ipv6_proto },
{ .off = 8, .val = cxgb4_fill_ipv6_src_ip0 },
{ .off = 12, .val = cxgb4_fill_ipv6_src_ip1 },
{ .off = 16, .val = cxgb4_fill_ipv6_src_ip2 },
{ .off = 20, .val = cxgb4_fill_ipv6_src_ip3 },
{ .off = 24, .val = cxgb4_fill_ipv6_dst_ip0 },
{ .off = 28, .val = cxgb4_fill_ipv6_dst_ip1 },
{ .off = 32, .val = cxgb4_fill_ipv6_dst_ip2 },
{ .off = 36, .val = cxgb4_fill_ipv6_dst_ip3 },
{ .val = NULL }
};
/* TCP/UDP match */
static inline int cxgb4_fill_l4_ports(struct ch_filter_specification *f,
__be32 val, __be32 mask)
{
f->val.fport = ntohl(val) >> 16;
f->mask.fport = ntohl(mask) >> 16;
f->val.lport = ntohl(val) & 0x0000FFFF;
f->mask.lport = ntohl(mask) & 0x0000FFFF;
return 0;
};
static const struct cxgb4_match_field cxgb4_tcp_fields[] = {
{ .off = 0, .val = cxgb4_fill_l4_ports },
{ .val = NULL }
};
static const struct cxgb4_match_field cxgb4_udp_fields[] = {
{ .off = 0, .val = cxgb4_fill_l4_ports },
{ .val = NULL }
};
struct cxgb4_next_header {
/* Offset, shift, and mask added to beginning of the header
* to get to next header. Useful when using a header
* field's value to jump to next header such as IHL field
* in IPv4 header.
*/
struct tc_u32_sel_hdr sel;
struct tc_u32_key key;
/* location of jump to make */
const struct cxgb4_match_field *jump;
};
/* Accept a rule with a jump to transport layer header based on IHL field in
* IPv4 header.
*/
static const struct cxgb4_next_header cxgb4_ipv4_jumps[] = {
{
/* TCP Jump */
.sel = {
.off = 0,
.offoff = 0,
.offshift = 6,
.offmask = cpu_to_be16(0x0f00),
},
.key = {
.off = 8,
.val = cpu_to_be32(0x00060000),
.mask = cpu_to_be32(0x00ff0000),
},
.jump = cxgb4_tcp_fields,
},
{
/* UDP Jump */
.sel = {
.off = 0,
.offoff = 0,
.offshift = 6,
.offmask = cpu_to_be16(0x0f00),
},
.key = {
.off = 8,
.val = cpu_to_be32(0x00110000),
.mask = cpu_to_be32(0x00ff0000),
},
.jump = cxgb4_udp_fields,
},
{ .jump = NULL },
};
/* Accept a rule with a jump directly past the 40 Bytes of IPv6 fixed header
* to get to transport layer header.
*/
static const struct cxgb4_next_header cxgb4_ipv6_jumps[] = {
{
/* TCP Jump */
.sel = {
.off = 40,
.offoff = 0,
.offshift = 0,
.offmask = 0,
},
.key = {
.off = 4,
.val = cpu_to_be32(0x00000600),
.mask = cpu_to_be32(0x0000ff00),
},
.jump = cxgb4_tcp_fields,
},
{
/* UDP Jump */
.sel = {
.off = 40,
.offoff = 0,
.offshift = 0,
.offmask = 0,
},
.key = {
.off = 4,
.val = cpu_to_be32(0x00001100),
.mask = cpu_to_be32(0x0000ff00),
},
.jump = cxgb4_udp_fields,
},
{ .jump = NULL },
};
struct cxgb4_link {
const struct cxgb4_match_field *match_field; /* Next header */
struct ch_filter_specification fs; /* Match spec associated with link */
u32 link_handle; /* Knode handle associated with the link */
unsigned long *tid_map; /* Bitmap for filter tids */
};
struct cxgb4_tc_u32_table {
unsigned int size; /* number of entries in table */
struct cxgb4_link table[] __counted_by(size); /* Jump table */
};
#endif /* __CXGB4_TC_U32_PARSE_H */
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2017 Chelsio Communications. All rights reserved.
*
* Written by: Ganesh Goudar (ganeshgr@chelsio.com)
*/
#include "cxgb4.h"
#define CXGB4_NUM_TRIPS 1
static int cxgb4_thermal_get_temp(struct thermal_zone_device *tzdev,
int *temp)
{
struct adapter *adap = thermal_zone_device_priv(tzdev);
u32 param, val;
int ret;
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DIAG) |
FW_PARAMS_PARAM_Y_V(FW_PARAM_DEV_DIAG_TMP));
ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1,
&param, &val);
if (ret < 0 || val == 0)
return -1;
*temp = val * 1000;
return 0;
}
static const struct thermal_zone_device_ops cxgb4_thermal_ops = {
.get_temp = cxgb4_thermal_get_temp,
};
static struct thermal_trip trip = { .type = THERMAL_TRIP_CRITICAL } ;
int cxgb4_thermal_init(struct adapter *adap)
{
struct ch_thermal *ch_thermal = &adap->ch_thermal;
char ch_tz_name[THERMAL_NAME_LENGTH];
int num_trip = CXGB4_NUM_TRIPS;
u32 param, val;
int ret;
/* on older firmwares we may not get the trip temperature,
* set the num of trips to 0.
*/
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DIAG) |
FW_PARAMS_PARAM_Y_V(FW_PARAM_DEV_DIAG_MAXTMPTHRESH));
ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1,
&param, &val);
if (ret < 0) {
num_trip = 0; /* could not get trip temperature */
} else {
trip.temperature = val * 1000;
}
snprintf(ch_tz_name, sizeof(ch_tz_name), "cxgb4_%s", adap->name);
ch_thermal->tzdev = thermal_zone_device_register_with_trips(ch_tz_name, &trip, num_trip,
adap,
&cxgb4_thermal_ops,
NULL, 0, 0);
if (IS_ERR(ch_thermal->tzdev)) {
ret = PTR_ERR(ch_thermal->tzdev);
dev_err(adap->pdev_dev, "Failed to register thermal zone\n");
ch_thermal->tzdev = NULL;
return ret;
}
ret = thermal_zone_device_enable(ch_thermal->tzdev);
if (ret) {
dev_err(adap->pdev_dev, "Failed to enable thermal zone\n");
thermal_zone_device_unregister(adap->ch_thermal.tzdev);
return ret;
}
return 0;
}
int cxgb4_thermal_remove(struct adapter *adap)
{
if (adap->ch_thermal.tzdev) {
thermal_zone_device_unregister(adap->ch_thermal.tzdev);
adap->ch_thermal.tzdev = NULL;
}
return 0;
}
@@ -0,0 +1,853 @@
/*
* cxgb4_uld.c:Chelsio Upper Layer Driver Interface for T4/T5/T6 SGE management
*
* Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Written by: Atul Gupta (atul.gupta@chelsio.com)
* Written by: Hariprasad Shenai (hariprasad@chelsio.com)
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/debugfs.h>
#include <linux/export.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/pci.h>
#include "cxgb4.h"
#include "cxgb4_uld.h"
#include "t4_regs.h"
#include "t4fw_api.h"
#include "t4_msg.h"
#define for_each_uldrxq(m, i) for (i = 0; i < ((m)->nrxq + (m)->nciq); i++)
/* Flush the aggregated lro sessions */
static void uldrx_flush_handler(struct sge_rspq *q)
{
struct adapter *adap = q->adap;
if (adap->uld[q->uld].lro_flush)
adap->uld[q->uld].lro_flush(&q->lro_mgr);
}
/**
* uldrx_handler - response queue handler for ULD queues
* @q: the response queue that received the packet
* @rsp: the response queue descriptor holding the offload message
* @gl: the gather list of packet fragments
*
* Deliver an ingress offload packet to a ULD. All processing is done by
* the ULD, we just maintain statistics.
*/
static int uldrx_handler(struct sge_rspq *q, const __be64 *rsp,
const struct pkt_gl *gl)
{
struct adapter *adap = q->adap;
struct sge_ofld_rxq *rxq = container_of(q, struct sge_ofld_rxq, rspq);
int ret;
/* FW can send CPLs encapsulated in a CPL_FW4_MSG */
if (((const struct rss_header *)rsp)->opcode == CPL_FW4_MSG &&
((const struct cpl_fw4_msg *)(rsp + 1))->type == FW_TYPE_RSSCPL)
rsp += 2;
if (q->flush_handler)
ret = adap->uld[q->uld].lro_rx_handler(adap->uld[q->uld].handle,
rsp, gl, &q->lro_mgr,
&q->napi);
else
ret = adap->uld[q->uld].rx_handler(adap->uld[q->uld].handle,
rsp, gl);
if (ret) {
rxq->stats.nomem++;
return -1;
}
if (!gl)
rxq->stats.imm++;
else if (gl == CXGB4_MSG_AN)
rxq->stats.an++;
else
rxq->stats.pkts++;
return 0;
}
static int alloc_uld_rxqs(struct adapter *adap,
struct sge_uld_rxq_info *rxq_info, bool lro)
{
unsigned int nq = rxq_info->nrxq + rxq_info->nciq;
struct sge_ofld_rxq *q = rxq_info->uldrxq;
unsigned short *ids = rxq_info->rspq_id;
int i, err, msi_idx, que_idx = 0;
struct sge *s = &adap->sge;
unsigned int per_chan;
per_chan = rxq_info->nrxq / adap->params.nports;
if (adap->flags & CXGB4_USING_MSIX)
msi_idx = 1;
else
msi_idx = -((int)s->intrq.abs_id + 1);
for (i = 0; i < nq; i++, q++) {
if (i == rxq_info->nrxq) {
/* start allocation of concentrator queues */
per_chan = rxq_info->nciq / adap->params.nports;
que_idx = 0;
}
if (msi_idx >= 0) {
msi_idx = cxgb4_get_msix_idx_from_bmap(adap);
if (msi_idx < 0) {
err = -ENOSPC;
goto freeout;
}
snprintf(adap->msix_info[msi_idx].desc,
sizeof(adap->msix_info[msi_idx].desc),
"%s-%s%d",
adap->port[0]->name, rxq_info->name, i);
q->msix = &adap->msix_info[msi_idx];
}
err = t4_sge_alloc_rxq(adap, &q->rspq, false,
adap->port[que_idx++ / per_chan],
msi_idx,
q->fl.size ? &q->fl : NULL,
uldrx_handler,
lro ? uldrx_flush_handler : NULL,
0);
if (err)
goto freeout;
memset(&q->stats, 0, sizeof(q->stats));
if (ids)
ids[i] = q->rspq.abs_id;
}
return 0;
freeout:
q = rxq_info->uldrxq;
for ( ; i; i--, q++) {
if (q->rspq.desc)
free_rspq_fl(adap, &q->rspq,
q->fl.size ? &q->fl : NULL);
if (q->msix)
cxgb4_free_msix_idx_in_bmap(adap, q->msix->idx);
}
return err;
}
static int
setup_sge_queues_uld(struct adapter *adap, unsigned int uld_type, bool lro)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
int i, ret;
ret = alloc_uld_rxqs(adap, rxq_info, lro);
if (ret)
return ret;
/* Tell uP to route control queue completions to rdma rspq */
if (adap->flags & CXGB4_FULL_INIT_DONE && uld_type == CXGB4_ULD_RDMA) {
struct sge *s = &adap->sge;
unsigned int cmplqid;
u32 param, cmdop;
cmdop = FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_CTRL;
for_each_port(adap, i) {
cmplqid = rxq_info->uldrxq[i].rspq.cntxt_id;
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X_V(cmdop) |
FW_PARAMS_PARAM_YZ_V(s->ctrlq[i].q.cntxt_id));
ret = t4_set_params(adap, adap->mbox, adap->pf,
0, 1, &param, &cmplqid);
}
}
return ret;
}
static void t4_free_uld_rxqs(struct adapter *adap, int n,
struct sge_ofld_rxq *q)
{
for ( ; n; n--, q++) {
if (q->rspq.desc)
free_rspq_fl(adap, &q->rspq,
q->fl.size ? &q->fl : NULL);
}
}
static void free_sge_queues_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
if (adap->flags & CXGB4_FULL_INIT_DONE && uld_type == CXGB4_ULD_RDMA) {
struct sge *s = &adap->sge;
u32 param, cmdop, cmplqid = 0;
int i;
cmdop = FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_CTRL;
for_each_port(adap, i) {
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X_V(cmdop) |
FW_PARAMS_PARAM_YZ_V(s->ctrlq[i].q.cntxt_id));
t4_set_params(adap, adap->mbox, adap->pf,
0, 1, &param, &cmplqid);
}
}
if (rxq_info->nciq)
t4_free_uld_rxqs(adap, rxq_info->nciq,
rxq_info->uldrxq + rxq_info->nrxq);
t4_free_uld_rxqs(adap, rxq_info->nrxq, rxq_info->uldrxq);
}
static int cfg_queues_uld(struct adapter *adap, unsigned int uld_type,
const struct cxgb4_uld_info *uld_info)
{
struct sge *s = &adap->sge;
struct sge_uld_rxq_info *rxq_info;
int i, nrxq, ciq_size;
rxq_info = kzalloc_obj(*rxq_info);
if (!rxq_info)
return -ENOMEM;
if (adap->flags & CXGB4_USING_MSIX && uld_info->nrxq > s->nqs_per_uld) {
i = s->nqs_per_uld;
rxq_info->nrxq = roundup(i, adap->params.nports);
} else {
i = min_t(int, uld_info->nrxq,
num_online_cpus());
rxq_info->nrxq = roundup(i, adap->params.nports);
}
if (!uld_info->ciq) {
rxq_info->nciq = 0;
} else {
if (adap->flags & CXGB4_USING_MSIX)
rxq_info->nciq = min_t(int, s->nqs_per_uld,
num_online_cpus());
else
rxq_info->nciq = min_t(int, MAX_OFLD_QSETS,
num_online_cpus());
rxq_info->nciq = ((rxq_info->nciq / adap->params.nports) *
adap->params.nports);
rxq_info->nciq = max_t(int, rxq_info->nciq,
adap->params.nports);
}
nrxq = rxq_info->nrxq + rxq_info->nciq; /* total rxq's */
rxq_info->uldrxq = kzalloc_objs(struct sge_ofld_rxq, nrxq);
if (!rxq_info->uldrxq) {
kfree(rxq_info);
return -ENOMEM;
}
rxq_info->rspq_id = kcalloc(nrxq, sizeof(unsigned short), GFP_KERNEL);
if (!rxq_info->rspq_id) {
kfree(rxq_info->uldrxq);
kfree(rxq_info);
return -ENOMEM;
}
for (i = 0; i < rxq_info->nrxq; i++) {
struct sge_ofld_rxq *r = &rxq_info->uldrxq[i];
init_rspq(adap, &r->rspq, 5, 1, uld_info->rxq_size, 64);
r->rspq.uld = uld_type;
r->fl.size = 72;
}
ciq_size = 64 + adap->vres.cq.size + adap->tids.nftids;
if (ciq_size > SGE_MAX_IQ_SIZE) {
dev_warn(adap->pdev_dev, "CIQ size too small for available IQs\n");
ciq_size = SGE_MAX_IQ_SIZE;
}
for (i = rxq_info->nrxq; i < nrxq; i++) {
struct sge_ofld_rxq *r = &rxq_info->uldrxq[i];
init_rspq(adap, &r->rspq, 5, 1, ciq_size, 64);
r->rspq.uld = uld_type;
}
memcpy(rxq_info->name, uld_info->name, IFNAMSIZ);
adap->sge.uld_rxq_info[uld_type] = rxq_info;
return 0;
}
static void free_queues_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
adap->sge.uld_rxq_info[uld_type] = NULL;
kfree(rxq_info->rspq_id);
kfree(rxq_info->uldrxq);
kfree(rxq_info);
}
static int
request_msix_queue_irqs_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
struct msix_info *minfo;
unsigned int idx;
int err = 0;
for_each_uldrxq(rxq_info, idx) {
minfo = rxq_info->uldrxq[idx].msix;
err = request_irq(minfo->vec,
t4_sge_intr_msix, 0,
minfo->desc,
&rxq_info->uldrxq[idx].rspq);
if (err)
goto unwind;
cxgb4_set_msix_aff(adap, minfo->vec,
&minfo->aff_mask, idx);
}
return 0;
unwind:
while (idx-- > 0) {
minfo = rxq_info->uldrxq[idx].msix;
cxgb4_clear_msix_aff(minfo->vec, minfo->aff_mask);
cxgb4_free_msix_idx_in_bmap(adap, minfo->idx);
free_irq(minfo->vec, &rxq_info->uldrxq[idx].rspq);
}
return err;
}
static void
free_msix_queue_irqs_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
struct msix_info *minfo;
unsigned int idx;
for_each_uldrxq(rxq_info, idx) {
minfo = rxq_info->uldrxq[idx].msix;
cxgb4_clear_msix_aff(minfo->vec, minfo->aff_mask);
cxgb4_free_msix_idx_in_bmap(adap, minfo->idx);
free_irq(minfo->vec, &rxq_info->uldrxq[idx].rspq);
}
}
static void enable_rx_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
int idx;
for_each_uldrxq(rxq_info, idx) {
struct sge_rspq *q = &rxq_info->uldrxq[idx].rspq;
if (!q)
continue;
cxgb4_enable_rx(adap, q);
}
}
static void quiesce_rx_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
int idx;
for_each_uldrxq(rxq_info, idx) {
struct sge_rspq *q = &rxq_info->uldrxq[idx].rspq;
if (!q)
continue;
cxgb4_quiesce_rx(q);
}
}
static void
free_sge_txq_uld(struct adapter *adap, struct sge_uld_txq_info *txq_info)
{
int nq = txq_info->ntxq;
int i;
for (i = 0; i < nq; i++) {
struct sge_uld_txq *txq = &txq_info->uldtxq[i];
if (txq->q.desc) {
tasklet_kill(&txq->qresume_tsk);
t4_ofld_eq_free(adap, adap->mbox, adap->pf, 0,
txq->q.cntxt_id);
free_tx_desc(adap, &txq->q, txq->q.in_use, false);
kfree(txq->q.sdesc);
__skb_queue_purge(&txq->sendq);
free_txq(adap, &txq->q);
}
}
}
static int
alloc_sge_txq_uld(struct adapter *adap, struct sge_uld_txq_info *txq_info,
unsigned int uld_type)
{
struct sge *s = &adap->sge;
int nq = txq_info->ntxq;
int i, j, err;
j = nq / adap->params.nports;
for (i = 0; i < nq; i++) {
struct sge_uld_txq *txq = &txq_info->uldtxq[i];
txq->q.size = 1024;
err = t4_sge_alloc_uld_txq(adap, txq, adap->port[i / j],
s->fw_evtq.cntxt_id, uld_type);
if (err)
goto freeout;
}
return 0;
freeout:
free_sge_txq_uld(adap, txq_info);
return err;
}
static void
release_sge_txq_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_txq_info *txq_info = NULL;
int tx_uld_type = TX_ULD(uld_type);
txq_info = adap->sge.uld_txq_info[tx_uld_type];
if (txq_info && atomic_dec_and_test(&txq_info->users)) {
free_sge_txq_uld(adap, txq_info);
kfree(txq_info->uldtxq);
kfree(txq_info);
adap->sge.uld_txq_info[tx_uld_type] = NULL;
}
}
static int
setup_sge_txq_uld(struct adapter *adap, unsigned int uld_type,
const struct cxgb4_uld_info *uld_info)
{
struct sge_uld_txq_info *txq_info = NULL;
int tx_uld_type, i;
tx_uld_type = TX_ULD(uld_type);
txq_info = adap->sge.uld_txq_info[tx_uld_type];
if ((tx_uld_type == CXGB4_TX_OFLD) && txq_info &&
(atomic_inc_return(&txq_info->users) > 1))
return 0;
txq_info = kzalloc_obj(*txq_info);
if (!txq_info)
return -ENOMEM;
if (uld_type == CXGB4_ULD_CRYPTO) {
i = min_t(int, adap->vres.ncrypto_fc,
num_online_cpus());
txq_info->ntxq = rounddown(i, adap->params.nports);
if (txq_info->ntxq <= 0) {
dev_warn(adap->pdev_dev, "Crypto Tx Queues can't be zero\n");
kfree(txq_info);
return -EINVAL;
}
} else {
i = min_t(int, uld_info->ntxq, num_online_cpus());
txq_info->ntxq = roundup(i, adap->params.nports);
}
txq_info->uldtxq = kzalloc_objs(struct sge_uld_txq, txq_info->ntxq);
if (!txq_info->uldtxq) {
kfree(txq_info);
return -ENOMEM;
}
if (alloc_sge_txq_uld(adap, txq_info, tx_uld_type)) {
kfree(txq_info->uldtxq);
kfree(txq_info);
return -ENOMEM;
}
atomic_inc(&txq_info->users);
adap->sge.uld_txq_info[tx_uld_type] = txq_info;
return 0;
}
static void uld_queue_init(struct adapter *adap, unsigned int uld_type,
struct cxgb4_lld_info *lli)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
int tx_uld_type = TX_ULD(uld_type);
struct sge_uld_txq_info *txq_info = adap->sge.uld_txq_info[tx_uld_type];
lli->rxq_ids = rxq_info->rspq_id;
lli->nrxq = rxq_info->nrxq;
lli->ciq_ids = rxq_info->rspq_id + rxq_info->nrxq;
lli->nciq = rxq_info->nciq;
lli->ntxq = txq_info->ntxq;
}
int t4_uld_mem_alloc(struct adapter *adap)
{
struct sge *s = &adap->sge;
adap->uld = kzalloc_objs(*adap->uld, CXGB4_ULD_MAX);
if (!adap->uld)
return -ENOMEM;
s->uld_rxq_info = kzalloc_objs(struct sge_uld_rxq_info *, CXGB4_ULD_MAX);
if (!s->uld_rxq_info)
goto err_uld;
s->uld_txq_info = kzalloc_objs(struct sge_uld_txq_info *, CXGB4_TX_MAX);
if (!s->uld_txq_info)
goto err_uld_rx;
return 0;
err_uld_rx:
kfree(s->uld_rxq_info);
err_uld:
kfree(adap->uld);
return -ENOMEM;
}
void t4_uld_mem_free(struct adapter *adap)
{
struct sge *s = &adap->sge;
kfree(s->uld_txq_info);
kfree(s->uld_rxq_info);
kfree(adap->uld);
}
/* This function should be called with uld_mutex taken. */
static void cxgb4_shutdown_uld_adapter(struct adapter *adap, enum cxgb4_uld type)
{
if (adap->uld[type].handle) {
adap->uld[type].handle = NULL;
adap->uld[type].add = NULL;
release_sge_txq_uld(adap, type);
if (adap->flags & CXGB4_FULL_INIT_DONE)
quiesce_rx_uld(adap, type);
if (adap->flags & CXGB4_USING_MSIX)
free_msix_queue_irqs_uld(adap, type);
free_sge_queues_uld(adap, type);
free_queues_uld(adap, type);
}
}
void t4_uld_clean_up(struct adapter *adap)
{
unsigned int i;
if (!is_uld(adap))
return;
mutex_lock(&uld_mutex);
for (i = 0; i < CXGB4_ULD_MAX; i++) {
if (!adap->uld[i].handle)
continue;
cxgb4_shutdown_uld_adapter(adap, i);
}
mutex_unlock(&uld_mutex);
}
static void uld_init(struct adapter *adap, struct cxgb4_lld_info *lld)
{
int i;
lld->pdev = adap->pdev;
lld->pf = adap->pf;
lld->l2t = adap->l2t;
lld->tids = &adap->tids;
lld->ports = adap->port;
lld->vr = &adap->vres;
lld->mtus = adap->params.mtus;
lld->nchan = adap->params.nports;
lld->nports = adap->params.nports;
lld->wr_cred = adap->params.ofldq_wr_cred;
lld->crypto = adap->params.crypto;
lld->iscsi_iolen = MAXRXDATA_G(t4_read_reg(adap, TP_PARA_REG2_A));
lld->iscsi_tagmask = t4_read_reg(adap, ULP_RX_ISCSI_TAGMASK_A);
lld->iscsi_pgsz_order = t4_read_reg(adap, ULP_RX_ISCSI_PSZ_A);
lld->iscsi_llimit = t4_read_reg(adap, ULP_RX_ISCSI_LLIMIT_A);
lld->iscsi_ppm = &adap->iscsi_ppm;
lld->adapter_type = adap->params.chip;
lld->cclk_ps = 1000000000 / adap->params.vpd.cclk;
lld->udb_density = 1 << adap->params.sge.eq_qpp;
lld->ucq_density = 1 << adap->params.sge.iq_qpp;
lld->sge_host_page_size = 1 << (adap->params.sge.hps + 10);
lld->filt_mode = adap->params.tp.vlan_pri_map;
/* MODQ_REQ_MAP sets queues 0-3 to chan 0-3 */
for (i = 0; i < NCHAN; i++)
lld->tx_modq[i] = i;
lld->gts_reg = adap->regs + MYPF_REG(SGE_PF_GTS_A);
lld->db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL_A);
lld->fw_vers = adap->params.fw_vers;
lld->dbfifo_int_thresh = dbfifo_int_thresh;
lld->sge_ingpadboundary = adap->sge.fl_align;
lld->sge_egrstatuspagesize = adap->sge.stat_len;
lld->sge_pktshift = adap->sge.pktshift;
lld->ulp_crypto = adap->params.crypto;
lld->enable_fw_ofld_conn = adap->flags & CXGB4_FW_OFLD_CONN;
lld->max_ordird_qp = adap->params.max_ordird_qp;
lld->max_ird_adapter = adap->params.max_ird_adapter;
lld->ulptx_memwrite_dsgl = adap->params.ulptx_memwrite_dsgl;
lld->nodeid = dev_to_node(adap->pdev_dev);
lld->fr_nsmr_tpte_wr_support = adap->params.fr_nsmr_tpte_wr_support;
lld->write_w_imm_support = adap->params.write_w_imm_support;
lld->write_cmpl_support = adap->params.write_cmpl_support;
}
static int uld_attach(struct adapter *adap, unsigned int uld)
{
struct cxgb4_lld_info lli;
void *handle;
uld_init(adap, &lli);
uld_queue_init(adap, uld, &lli);
handle = adap->uld[uld].add(&lli);
if (IS_ERR(handle)) {
dev_warn(adap->pdev_dev,
"could not attach to the %s driver, error %ld\n",
adap->uld[uld].name, PTR_ERR(handle));
return PTR_ERR(handle);
}
adap->uld[uld].handle = handle;
t4_register_netevent_notifier();
if (adap->flags & CXGB4_FULL_INIT_DONE)
adap->uld[uld].state_change(handle, CXGB4_STATE_UP);
return 0;
}
#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE)
static bool cxgb4_uld_in_use(struct adapter *adap)
{
const struct tid_info *t = &adap->tids;
return (atomic_read(&t->conns_in_use) || t->stids_in_use);
}
/* cxgb4_set_ktls_feature: request FW to enable/disable ktls settings.
* @adap: adapter info
* @enable: 1 to enable / 0 to disable ktls settings.
*/
int cxgb4_set_ktls_feature(struct adapter *adap, bool enable)
{
int ret = 0;
u32 params =
FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_KTLS_HW) |
FW_PARAMS_PARAM_Y_V(enable) |
FW_PARAMS_PARAM_Z_V(FW_PARAMS_PARAM_DEV_KTLS_HW_USER_ENABLE);
if (enable) {
if (!refcount_read(&adap->chcr_ktls.ktls_refcount)) {
/* At this moment if ULD connection are up means, other
* ULD is/are already active, return failure.
*/
if (cxgb4_uld_in_use(adap)) {
dev_dbg(adap->pdev_dev,
"ULD connections (tid/stid) active. Can't enable kTLS\n");
return -EINVAL;
}
ret = t4_set_params(adap, adap->mbox, adap->pf,
0, 1, &params, &params);
if (ret)
return ret;
refcount_set(&adap->chcr_ktls.ktls_refcount, 1);
pr_debug("kTLS has been enabled. Restrictions placed on ULD support\n");
} else {
/* ktls settings already up, just increment refcount. */
refcount_inc(&adap->chcr_ktls.ktls_refcount);
}
} else {
/* return failure if refcount is already 0. */
if (!refcount_read(&adap->chcr_ktls.ktls_refcount))
return -EINVAL;
/* decrement refcount and test, if 0, disable ktls feature,
* else return command success.
*/
if (refcount_dec_and_test(&adap->chcr_ktls.ktls_refcount)) {
ret = t4_set_params(adap, adap->mbox, adap->pf,
0, 1, &params, &params);
if (ret)
return ret;
pr_debug("kTLS is disabled. Restrictions on ULD support removed\n");
}
}
return ret;
}
#endif
static void cxgb4_uld_alloc_resources(struct adapter *adap,
enum cxgb4_uld type,
const struct cxgb4_uld_info *p)
{
int ret = 0;
if ((type == CXGB4_ULD_CRYPTO && !is_pci_uld(adap)) ||
(type != CXGB4_ULD_CRYPTO && !is_offload(adap)))
return;
if (type == CXGB4_ULD_ISCSIT && is_t4(adap->params.chip))
return;
ret = cfg_queues_uld(adap, type, p);
if (ret)
goto out;
ret = setup_sge_queues_uld(adap, type, p->lro);
if (ret)
goto free_queues;
if (adap->flags & CXGB4_USING_MSIX) {
ret = request_msix_queue_irqs_uld(adap, type);
if (ret)
goto free_rxq;
}
if (adap->flags & CXGB4_FULL_INIT_DONE)
enable_rx_uld(adap, type);
if (adap->uld[type].add)
goto free_irq;
ret = setup_sge_txq_uld(adap, type, p);
if (ret)
goto free_irq;
adap->uld[type] = *p;
ret = uld_attach(adap, type);
if (ret)
goto free_txq;
return;
free_txq:
release_sge_txq_uld(adap, type);
free_irq:
if (adap->flags & CXGB4_FULL_INIT_DONE)
quiesce_rx_uld(adap, type);
if (adap->flags & CXGB4_USING_MSIX)
free_msix_queue_irqs_uld(adap, type);
free_rxq:
free_sge_queues_uld(adap, type);
free_queues:
free_queues_uld(adap, type);
out:
dev_warn(adap->pdev_dev,
"ULD registration failed for uld type %d\n", type);
}
void cxgb4_uld_enable(struct adapter *adap)
{
struct cxgb4_uld_list *uld_entry;
mutex_lock(&uld_mutex);
list_add_tail(&adap->list_node, &adapter_list);
list_for_each_entry(uld_entry, &uld_list, list_node)
cxgb4_uld_alloc_resources(adap, uld_entry->uld_type,
&uld_entry->uld_info);
mutex_unlock(&uld_mutex);
}
/* cxgb4_register_uld - register an upper-layer driver
* @type: the ULD type
* @p: the ULD methods
*
* Registers an upper-layer driver with this driver and notifies the ULD
* about any presently available devices that support its type.
*/
void cxgb4_register_uld(enum cxgb4_uld type,
const struct cxgb4_uld_info *p)
{
struct cxgb4_uld_list *uld_entry;
struct adapter *adap;
if (type >= CXGB4_ULD_MAX)
return;
uld_entry = kzalloc_obj(*uld_entry);
if (!uld_entry)
return;
memcpy(&uld_entry->uld_info, p, sizeof(struct cxgb4_uld_info));
mutex_lock(&uld_mutex);
list_for_each_entry(adap, &adapter_list, list_node)
cxgb4_uld_alloc_resources(adap, type, p);
uld_entry->uld_type = type;
list_add_tail(&uld_entry->list_node, &uld_list);
mutex_unlock(&uld_mutex);
return;
}
EXPORT_SYMBOL(cxgb4_register_uld);
/**
* cxgb4_unregister_uld - unregister an upper-layer driver
* @type: the ULD type
*
* Unregisters an existing upper-layer driver.
*/
int cxgb4_unregister_uld(enum cxgb4_uld type)
{
struct cxgb4_uld_list *uld_entry, *tmp;
struct adapter *adap;
if (type >= CXGB4_ULD_MAX)
return -EINVAL;
mutex_lock(&uld_mutex);
list_for_each_entry(adap, &adapter_list, list_node) {
if ((type == CXGB4_ULD_CRYPTO && !is_pci_uld(adap)) ||
(type != CXGB4_ULD_CRYPTO && !is_offload(adap)))
continue;
if (type == CXGB4_ULD_ISCSIT && is_t4(adap->params.chip))
continue;
cxgb4_shutdown_uld_adapter(adap, type);
}
list_for_each_entry_safe(uld_entry, tmp, &uld_list, list_node) {
if (uld_entry->uld_type == type) {
list_del(&uld_entry->list_node);
kfree(uld_entry);
}
}
mutex_unlock(&uld_mutex);
return 0;
}
EXPORT_SYMBOL(cxgb4_unregister_uld);
@@ -0,0 +1,536 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_ULD_H
#define __CXGB4_ULD_H
#include <linux/cache.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
#include <linux/inetdevice.h>
#include <linux/atomic.h>
#include <net/tls.h>
#include "cxgb4.h"
#define MAX_ULD_QSETS 16
#define MAX_ULD_NPORTS 4
/* ulp_mem_io + ulptx_idata + payload + padding */
#define MAX_IMM_ULPTX_WR_LEN (32 + 8 + 256 + 8)
/* CPL message priority levels */
enum {
CPL_PRIORITY_DATA = 0, /* data messages */
CPL_PRIORITY_SETUP = 1, /* connection setup messages */
CPL_PRIORITY_TEARDOWN = 0, /* connection teardown messages */
CPL_PRIORITY_LISTEN = 1, /* listen start/stop messages */
CPL_PRIORITY_ACK = 1, /* RX ACK messages */
CPL_PRIORITY_CONTROL = 1 /* control messages */
};
#define INIT_TP_WR(w, tid) do { \
(w)->wr.wr_hi = htonl(FW_WR_OP_V(FW_TP_WR) | \
FW_WR_IMMDLEN_V(sizeof(*w) - sizeof(w->wr))); \
(w)->wr.wr_mid = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*w), 16)) | \
FW_WR_FLOWID_V(tid)); \
(w)->wr.wr_lo = cpu_to_be64(0); \
} while (0)
#define INIT_TP_WR_CPL(w, cpl, tid) do { \
INIT_TP_WR(w, tid); \
OPCODE_TID(w) = htonl(MK_OPCODE_TID(cpl, tid)); \
} while (0)
#define INIT_ULPTX_WR(w, wrlen, atomic, tid) do { \
(w)->wr.wr_hi = htonl(FW_WR_OP_V(FW_ULPTX_WR) | \
FW_WR_ATOMIC_V(atomic)); \
(w)->wr.wr_mid = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(wrlen, 16)) | \
FW_WR_FLOWID_V(tid)); \
(w)->wr.wr_lo = cpu_to_be64(0); \
} while (0)
/* Special asynchronous notification message */
#define CXGB4_MSG_AN ((void *)1)
#define TX_ULD(uld)(((uld) != CXGB4_ULD_CRYPTO) ? CXGB4_TX_OFLD :\
CXGB4_TX_CRYPTO)
struct serv_entry {
void *data;
};
union aopen_entry {
void *data;
union aopen_entry *next;
};
struct eotid_entry {
void *data;
};
/*
* Holds the size, base address, free list start, etc of the TID, server TID,
* and active-open TID tables. The tables themselves are allocated dynamically.
*/
struct tid_info {
void **tid_tab;
unsigned int tid_base;
unsigned int ntids;
struct serv_entry *stid_tab;
unsigned long *stid_bmap;
unsigned int nstids;
unsigned int stid_base;
unsigned int nhash;
unsigned int hash_base;
union aopen_entry *atid_tab;
unsigned int natids;
unsigned int atid_base;
struct filter_entry *hpftid_tab;
unsigned long *hpftid_bmap;
unsigned int nhpftids;
unsigned int hpftid_base;
struct filter_entry *ftid_tab;
unsigned long *ftid_bmap;
unsigned int nftids;
unsigned int ftid_base;
unsigned int aftid_base;
unsigned int aftid_end;
/* Server filter region */
unsigned int sftid_base;
unsigned int nsftids;
spinlock_t atid_lock ____cacheline_aligned_in_smp;
union aopen_entry *afree;
unsigned int atids_in_use;
spinlock_t stid_lock;
unsigned int stids_in_use;
unsigned int v6_stids_in_use;
unsigned int sftids_in_use;
/* ETHOFLD range */
struct eotid_entry *eotid_tab;
unsigned long *eotid_bmap;
unsigned int eotid_base;
unsigned int neotids;
/* TIDs in the TCAM */
atomic_t tids_in_use;
/* TIDs in the HASH */
atomic_t hash_tids_in_use;
atomic_t conns_in_use;
/* ETHOFLD TIDs used for rate limiting */
atomic_t eotids_in_use;
/* lock for setting/clearing filter bitmap */
spinlock_t ftid_lock;
unsigned int tc_hash_tids_max_prio;
};
static inline void *lookup_tid(const struct tid_info *t, unsigned int tid)
{
tid -= t->tid_base;
return tid < t->ntids ? t->tid_tab[tid] : NULL;
}
static inline bool tid_out_of_range(const struct tid_info *t, unsigned int tid)
{
return ((tid - t->tid_base) >= t->ntids);
}
static inline void *lookup_atid(const struct tid_info *t, unsigned int atid)
{
return atid < t->natids ? t->atid_tab[atid].data : NULL;
}
static inline void *lookup_stid(const struct tid_info *t, unsigned int stid)
{
/* Is it a server filter TID? */
if (t->nsftids && (stid >= t->sftid_base)) {
stid -= t->sftid_base;
stid += t->nstids;
} else {
stid -= t->stid_base;
}
return stid < (t->nstids + t->nsftids) ? t->stid_tab[stid].data : NULL;
}
static inline void cxgb4_insert_tid(struct tid_info *t, void *data,
unsigned int tid, unsigned short family)
{
t->tid_tab[tid - t->tid_base] = data;
if (t->hash_base && (tid >= t->hash_base)) {
if (family == AF_INET6)
atomic_add(2, &t->hash_tids_in_use);
else
atomic_inc(&t->hash_tids_in_use);
} else {
if (family == AF_INET6)
atomic_add(2, &t->tids_in_use);
else
atomic_inc(&t->tids_in_use);
}
atomic_inc(&t->conns_in_use);
}
static inline struct eotid_entry *cxgb4_lookup_eotid(struct tid_info *t,
u32 eotid)
{
return eotid < t->neotids ? &t->eotid_tab[eotid] : NULL;
}
static inline int cxgb4_get_free_eotid(struct tid_info *t)
{
int eotid;
eotid = find_first_zero_bit(t->eotid_bmap, t->neotids);
if (eotid >= t->neotids)
eotid = -1;
return eotid;
}
static inline void cxgb4_alloc_eotid(struct tid_info *t, u32 eotid, void *data)
{
set_bit(eotid, t->eotid_bmap);
t->eotid_tab[eotid].data = data;
atomic_inc(&t->eotids_in_use);
}
static inline void cxgb4_free_eotid(struct tid_info *t, u32 eotid)
{
clear_bit(eotid, t->eotid_bmap);
t->eotid_tab[eotid].data = NULL;
atomic_dec(&t->eotids_in_use);
}
int cxgb4_alloc_atid(struct tid_info *t, void *data);
int cxgb4_alloc_stid(struct tid_info *t, int family, void *data);
int cxgb4_alloc_sftid(struct tid_info *t, int family, void *data);
void cxgb4_free_atid(struct tid_info *t, unsigned int atid);
void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family);
void cxgb4_remove_tid(struct tid_info *t, unsigned int qid, unsigned int tid,
unsigned short family);
struct in6_addr;
int cxgb4_create_server(const struct net_device *dev, unsigned int stid,
__be32 sip, __be16 sport, __be16 vlan,
unsigned int queue);
int cxgb4_create_server6(const struct net_device *dev, unsigned int stid,
const struct in6_addr *sip, __be16 sport,
unsigned int queue);
int cxgb4_remove_server(const struct net_device *dev, unsigned int stid,
unsigned int queue, bool ipv6);
int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid,
__be32 sip, __be16 sport, __be16 vlan,
unsigned int queue,
unsigned char port, unsigned char mask);
int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid,
unsigned int queue, bool ipv6);
/* Filter operation context to allow callers of cxgb4_set_filter() and
* cxgb4_del_filter() to wait for an asynchronous completion.
*/
struct filter_ctx {
struct completion completion; /* completion rendezvous */
void *closure; /* caller's opaque information */
int result; /* result of operation */
u32 tid; /* to store tid */
};
struct chcr_ktls {
refcount_t ktls_refcount;
};
struct ch_filter_specification;
int cxgb4_get_free_ftid(struct net_device *dev, u8 family, bool hash_en,
u32 tc_prio);
int __cxgb4_set_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs,
struct filter_ctx *ctx);
int __cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs,
struct filter_ctx *ctx);
int cxgb4_set_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs);
int cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs);
int cxgb4_get_filter_counters(struct net_device *dev, unsigned int fidx,
u64 *hitcnt, u64 *bytecnt, bool hash);
static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue)
{
skb_set_queue_mapping(skb, (queue << 1) | prio);
}
enum cxgb4_uld {
CXGB4_ULD_INIT,
CXGB4_ULD_RDMA,
CXGB4_ULD_ISCSI,
CXGB4_ULD_ISCSIT,
CXGB4_ULD_CRYPTO,
CXGB4_ULD_IPSEC,
CXGB4_ULD_TLS,
CXGB4_ULD_KTLS,
CXGB4_ULD_MAX
};
enum cxgb4_tx_uld {
CXGB4_TX_OFLD,
CXGB4_TX_CRYPTO,
CXGB4_TX_MAX
};
enum cxgb4_txq_type {
CXGB4_TXQ_ETH,
CXGB4_TXQ_ULD,
CXGB4_TXQ_CTRL,
CXGB4_TXQ_MAX
};
enum cxgb4_state {
CXGB4_STATE_UP,
CXGB4_STATE_START_RECOVERY,
CXGB4_STATE_DOWN,
CXGB4_STATE_DETACH,
CXGB4_STATE_FATAL_ERROR
};
enum cxgb4_control {
CXGB4_CONTROL_DB_FULL,
CXGB4_CONTROL_DB_EMPTY,
CXGB4_CONTROL_DB_DROP,
};
struct adapter;
struct pci_dev;
struct l2t_data;
struct net_device;
struct pkt_gl;
struct tp_tcp_stats;
struct t4_lro_mgr;
struct cxgb4_range {
unsigned int start;
unsigned int size;
};
struct cxgb4_virt_res { /* virtualized HW resources */
struct cxgb4_range ddp;
struct cxgb4_range iscsi;
struct cxgb4_range stag;
struct cxgb4_range rq;
struct cxgb4_range srq;
struct cxgb4_range pbl;
struct cxgb4_range qp;
struct cxgb4_range cq;
struct cxgb4_range ocq;
struct cxgb4_range key;
unsigned int ncrypto_fc;
struct cxgb4_range ppod_edram;
};
#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE)
struct ch_ktls_port_stats_debug {
atomic64_t ktls_tx_connection_open;
atomic64_t ktls_tx_connection_fail;
atomic64_t ktls_tx_connection_close;
atomic64_t ktls_tx_encrypted_packets;
atomic64_t ktls_tx_encrypted_bytes;
atomic64_t ktls_tx_ctx;
atomic64_t ktls_tx_ooo;
atomic64_t ktls_tx_skip_no_sync_data;
atomic64_t ktls_tx_drop_no_sync_data;
atomic64_t ktls_tx_drop_bypass_req;
};
struct ch_ktls_stats_debug {
struct ch_ktls_port_stats_debug ktls_port[MAX_ULD_NPORTS];
atomic64_t ktls_tx_send_records;
atomic64_t ktls_tx_end_pkts;
atomic64_t ktls_tx_start_pkts;
atomic64_t ktls_tx_middle_pkts;
atomic64_t ktls_tx_retransmit_pkts;
atomic64_t ktls_tx_complete_pkts;
atomic64_t ktls_tx_trimmed_pkts;
atomic64_t ktls_tx_fallback;
};
#endif
struct chcr_stats_debug {
atomic_t cipher_rqst;
atomic_t digest_rqst;
atomic_t aead_rqst;
atomic_t complete;
atomic_t error;
atomic_t fallback;
atomic_t tls_pdu_tx;
atomic_t tls_pdu_rx;
atomic_t tls_key;
};
#if IS_ENABLED(CONFIG_CHELSIO_IPSEC_INLINE)
struct ch_ipsec_stats_debug {
atomic_t ipsec_cnt;
};
#endif
#define OCQ_WIN_OFFSET(pdev, vres) \
(pci_resource_len((pdev), 2) - roundup_pow_of_two((vres)->ocq.size))
/*
* Block of information the LLD provides to ULDs attaching to a device.
*/
struct cxgb4_lld_info {
struct pci_dev *pdev; /* associated PCI device */
struct l2t_data *l2t; /* L2 table */
struct tid_info *tids; /* TID table */
struct net_device **ports; /* device ports */
const struct cxgb4_virt_res *vr; /* assorted HW resources */
const unsigned short *mtus; /* MTU table */
const unsigned short *rxq_ids; /* the ULD's Rx queue ids */
const unsigned short *ciq_ids; /* the ULD's concentrator IQ ids */
unsigned short nrxq; /* # of Rx queues */
unsigned short ntxq; /* # of Tx queues */
unsigned short nciq; /* # of concentrator IQ */
unsigned char nchan:4; /* # of channels */
unsigned char nports:4; /* # of ports */
unsigned char wr_cred; /* WR 16-byte credits */
unsigned char adapter_type; /* type of adapter */
unsigned char fw_api_ver; /* FW API version */
unsigned char crypto; /* crypto support */
unsigned int fw_vers; /* FW version */
unsigned int iscsi_iolen; /* iSCSI max I/O length */
unsigned int cclk_ps; /* Core clock period in psec */
unsigned short udb_density; /* # of user DB/page */
unsigned short ucq_density; /* # of user CQs/page */
unsigned int sge_host_page_size; /* SGE host page size */
unsigned short filt_mode; /* filter optional components */
unsigned short tx_modq[NCHAN]; /* maps each tx channel to a */
/* scheduler queue */
void __iomem *gts_reg; /* address of GTS register */
void __iomem *db_reg; /* address of kernel doorbell */
int dbfifo_int_thresh; /* doorbell fifo int threshold */
unsigned int sge_ingpadboundary; /* SGE ingress padding boundary */
unsigned int sge_egrstatuspagesize; /* SGE egress status page size */
unsigned int sge_pktshift; /* Padding between CPL and */
/* packet data */
unsigned int pf; /* Physical Function we're using */
bool enable_fw_ofld_conn; /* Enable connection through fw */
/* WR */
unsigned int max_ordird_qp; /* Max ORD/IRD depth per RDMA QP */
unsigned int max_ird_adapter; /* Max IRD memory per adapter */
bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */
unsigned int iscsi_tagmask; /* iscsi ddp tag mask */
unsigned int iscsi_pgsz_order; /* iscsi ddp page size orders */
unsigned int iscsi_llimit; /* chip's iscsi region llimit */
unsigned int ulp_crypto; /* crypto lookaside support */
void **iscsi_ppm; /* iscsi page pod manager */
int nodeid; /* device numa node id */
bool fr_nsmr_tpte_wr_support; /* FW supports FR_NSMR_TPTE_WR */
bool write_w_imm_support; /* FW supports WRITE_WITH_IMMEDIATE */
bool write_cmpl_support; /* FW supports WRITE_CMPL WR */
};
struct cxgb4_uld_info {
char name[IFNAMSIZ];
void *handle;
unsigned int nrxq;
unsigned int rxq_size;
unsigned int ntxq;
bool ciq;
bool lro;
void *(*add)(const struct cxgb4_lld_info *p);
int (*rx_handler)(void *handle, const __be64 *rsp,
const struct pkt_gl *gl);
int (*state_change)(void *handle, enum cxgb4_state new_state);
int (*control)(void *handle, enum cxgb4_control control, ...);
int (*lro_rx_handler)(void *handle, const __be64 *rsp,
const struct pkt_gl *gl,
struct t4_lro_mgr *lro_mgr,
struct napi_struct *napi);
void (*lro_flush)(struct t4_lro_mgr *);
int (*tx_handler)(struct sk_buff *skb, struct net_device *dev);
#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE)
const struct tlsdev_ops *tlsdev_ops;
#endif
#if IS_ENABLED(CONFIG_XFRM_OFFLOAD)
const struct xfrmdev_ops *xfrmdev_ops;
#endif
};
void cxgb4_uld_enable(struct adapter *adap);
void cxgb4_register_uld(enum cxgb4_uld type, const struct cxgb4_uld_info *p);
int cxgb4_unregister_uld(enum cxgb4_uld type);
int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb);
int cxgb4_immdata_send(struct net_device *dev, unsigned int idx,
const void *src, unsigned int len);
int cxgb4_crypto_send(struct net_device *dev, struct sk_buff *skb);
unsigned int cxgb4_dbfifo_count(const struct net_device *dev, int lpfifo);
unsigned int cxgb4_port_chan(const struct net_device *dev);
unsigned int cxgb4_port_e2cchan(const struct net_device *dev);
unsigned int cxgb4_port_viid(const struct net_device *dev);
unsigned int cxgb4_port_idx(const struct net_device *dev);
unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu,
unsigned int *idx);
unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
unsigned short header_size,
unsigned short data_size_max,
unsigned short data_size_align,
unsigned int *mtu_idxp);
void cxgb4_get_tcp_stats(struct pci_dev *pdev, struct tp_tcp_stats *v4,
struct tp_tcp_stats *v6);
struct sk_buff *cxgb4_pktgl_to_skb(const struct pkt_gl *gl,
unsigned int skb_len, unsigned int pull_len);
int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx, u16 size);
int cxgb4_flush_eq_cache(struct net_device *dev);
int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte);
u64 cxgb4_read_sge_timestamp(struct net_device *dev);
enum cxgb4_bar2_qtype { CXGB4_BAR2_QTYPE_EGRESS, CXGB4_BAR2_QTYPE_INGRESS };
int cxgb4_bar2_sge_qregs(struct net_device *dev,
unsigned int qid,
enum cxgb4_bar2_qtype qtype,
int user,
u64 *pbar2_qoffset,
unsigned int *pbar2_qid);
#endif /* !__CXGB4_ULD_H */
@@ -0,0 +1,743 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/jhash.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <net/neighbour.h>
#include "cxgb4.h"
#include "l2t.h"
#include "t4_msg.h"
#include "t4fw_api.h"
#include "t4_regs.h"
#include "t4_values.h"
/* identifies sync vs async L2T_WRITE_REQs */
#define SYNC_WR_S 12
#define SYNC_WR_V(x) ((x) << SYNC_WR_S)
#define SYNC_WR_F SYNC_WR_V(1)
struct l2t_data {
unsigned int l2t_start; /* start index of our piece of the L2T */
unsigned int l2t_size; /* number of entries in l2tab */
rwlock_t lock;
atomic_t nfree; /* number of free entries */
struct l2t_entry *rover; /* starting point for next allocation */
struct l2t_entry l2tab[] __counted_by(l2t_size); /* MUST BE LAST */
};
static inline unsigned int vlan_prio(const struct l2t_entry *e)
{
return e->vlan >> VLAN_PRIO_SHIFT;
}
static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e)
{
if (atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */
atomic_dec(&d->nfree);
}
/*
* To avoid having to check address families we do not allow v4 and v6
* neighbors to be on the same hash chain. We keep v4 entries in the first
* half of available hash buckets and v6 in the second. We need at least two
* entries in our L2T for this scheme to work.
*/
enum {
L2T_MIN_HASH_BUCKETS = 2,
};
static inline unsigned int arp_hash(struct l2t_data *d, const u32 *key,
int ifindex)
{
unsigned int l2t_size_half = d->l2t_size / 2;
return jhash_2words(*key, ifindex, 0) % l2t_size_half;
}
static inline unsigned int ipv6_hash(struct l2t_data *d, const u32 *key,
int ifindex)
{
unsigned int l2t_size_half = d->l2t_size / 2;
u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3];
return (l2t_size_half +
(jhash_2words(xor, ifindex, 0) % l2t_size_half));
}
static unsigned int addr_hash(struct l2t_data *d, const u32 *addr,
int addr_len, int ifindex)
{
return addr_len == 4 ? arp_hash(d, addr, ifindex) :
ipv6_hash(d, addr, ifindex);
}
/*
* Checks if an L2T entry is for the given IP/IPv6 address. It does not check
* whether the L2T entry and the address are of the same address family.
* Callers ensure an address is only checked against L2T entries of the same
* family, something made trivial by the separation of IP and IPv6 hash chains
* mentioned above. Returns 0 if there's a match,
*/
static int addreq(const struct l2t_entry *e, const u32 *addr)
{
if (e->v6)
return (e->addr[0] ^ addr[0]) | (e->addr[1] ^ addr[1]) |
(e->addr[2] ^ addr[2]) | (e->addr[3] ^ addr[3]);
return e->addr[0] ^ addr[0];
}
static void neigh_replace(struct l2t_entry *e, struct neighbour *n)
{
neigh_hold(n);
if (e->neigh)
neigh_release(e->neigh);
e->neigh = n;
}
/*
* Write an L2T entry. Must be called with the entry locked.
* The write may be synchronous or asynchronous.
*/
static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync)
{
struct l2t_data *d = adap->l2t;
unsigned int l2t_idx = e->idx + d->l2t_start;
struct sk_buff *skb;
struct cpl_l2t_write_req *req;
skb = alloc_skb(sizeof(*req), GFP_ATOMIC);
if (!skb)
return -ENOMEM;
req = __skb_put(skb, sizeof(*req));
INIT_TP_WR(req, 0);
OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ,
l2t_idx | (sync ? SYNC_WR_F : 0) |
TID_QID_V(adap->sge.fw_evtq.abs_id)));
req->params = htons(L2T_W_PORT_V(e->lport) | L2T_W_NOREPLY_V(!sync));
req->l2t_idx = htons(l2t_idx);
req->vlan = htons(e->vlan);
if (e->neigh && !(e->neigh->dev->flags & IFF_LOOPBACK))
memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac));
memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac));
t4_mgmt_tx(adap, skb);
if (sync && e->state != L2T_STATE_SWITCHING)
e->state = L2T_STATE_SYNC_WRITE;
return 0;
}
/*
* Send packets waiting in an L2T entry's ARP queue. Must be called with the
* entry locked.
*/
static void send_pending(struct adapter *adap, struct l2t_entry *e)
{
struct sk_buff *skb;
while ((skb = __skb_dequeue(&e->arpq)) != NULL)
t4_ofld_send(adap, skb);
}
/*
* Process a CPL_L2T_WRITE_RPL. Wake up the ARP queue if it completes a
* synchronous L2T_WRITE. Note that the TID in the reply is really the L2T
* index it refers to.
*/
void do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl)
{
struct l2t_data *d = adap->l2t;
unsigned int tid = GET_TID(rpl);
unsigned int l2t_idx = tid % L2T_SIZE;
if (unlikely(rpl->status != CPL_ERR_NONE)) {
dev_err(adap->pdev_dev,
"Unexpected L2T_WRITE_RPL status %u for entry %u\n",
rpl->status, l2t_idx);
return;
}
if (tid & SYNC_WR_F) {
struct l2t_entry *e = &d->l2tab[l2t_idx - d->l2t_start];
spin_lock(&e->lock);
if (e->state != L2T_STATE_SWITCHING) {
send_pending(adap, e);
e->state = (e->neigh->nud_state & NUD_STALE) ?
L2T_STATE_STALE : L2T_STATE_VALID;
}
spin_unlock(&e->lock);
}
}
/*
* Add a packet to an L2T entry's queue of packets awaiting resolution.
* Must be called with the entry's lock held.
*/
static inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb)
{
__skb_queue_tail(&e->arpq, skb);
}
int cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb,
struct l2t_entry *e)
{
struct adapter *adap = netdev2adap(dev);
again:
switch (e->state) {
case L2T_STATE_STALE: /* entry is stale, kick off revalidation */
neigh_event_send(e->neigh, NULL);
spin_lock_bh(&e->lock);
if (e->state == L2T_STATE_STALE)
e->state = L2T_STATE_VALID;
spin_unlock_bh(&e->lock);
fallthrough;
case L2T_STATE_VALID: /* fast-path, send the packet on */
return t4_ofld_send(adap, skb);
case L2T_STATE_RESOLVING:
case L2T_STATE_SYNC_WRITE:
spin_lock_bh(&e->lock);
if (e->state != L2T_STATE_SYNC_WRITE &&
e->state != L2T_STATE_RESOLVING) {
spin_unlock_bh(&e->lock);
goto again;
}
arpq_enqueue(e, skb);
spin_unlock_bh(&e->lock);
if (e->state == L2T_STATE_RESOLVING &&
!neigh_event_send(e->neigh, NULL)) {
spin_lock_bh(&e->lock);
if (e->state == L2T_STATE_RESOLVING &&
!skb_queue_empty(&e->arpq))
write_l2e(adap, e, 1);
spin_unlock_bh(&e->lock);
}
}
return 0;
}
EXPORT_SYMBOL(cxgb4_l2t_send);
/*
* Allocate a free L2T entry. Must be called with l2t_data.lock held.
*/
static struct l2t_entry *alloc_l2e(struct l2t_data *d)
{
struct l2t_entry *end, *e, **p;
if (!atomic_read(&d->nfree))
return NULL;
/* there's definitely a free entry */
for (e = d->rover, end = &d->l2tab[d->l2t_size]; e != end; ++e)
if (atomic_read(&e->refcnt) == 0)
goto found;
for (e = d->l2tab; atomic_read(&e->refcnt); ++e)
;
found:
d->rover = e + 1;
atomic_dec(&d->nfree);
/*
* The entry we found may be an inactive entry that is
* presently in the hash table. We need to remove it.
*/
if (e->state < L2T_STATE_SWITCHING)
for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next)
if (*p == e) {
*p = e->next;
e->next = NULL;
break;
}
e->state = L2T_STATE_UNUSED;
return e;
}
static struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan,
u8 port, u8 *dmac)
{
struct l2t_entry *end, *e, **p;
struct l2t_entry *first_free = NULL;
for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) {
if (atomic_read(&e->refcnt) == 0) {
if (!first_free)
first_free = e;
} else {
if (e->state == L2T_STATE_SWITCHING) {
if (ether_addr_equal(e->dmac, dmac) &&
(e->vlan == vlan) && (e->lport == port))
goto exists;
}
}
}
if (first_free) {
e = first_free;
goto found;
}
return NULL;
found:
/* The entry we found may be an inactive entry that is
* presently in the hash table. We need to remove it.
*/
if (e->state < L2T_STATE_SWITCHING)
for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next)
if (*p == e) {
*p = e->next;
e->next = NULL;
break;
}
e->state = L2T_STATE_UNUSED;
exists:
return e;
}
/* Called when an L2T entry has no more users. The entry is left in the hash
* table since it is likely to be reused but we also bump nfree to indicate
* that the entry can be reallocated for a different neighbor. We also drop
* the existing neighbor reference in case the neighbor is going away and is
* waiting on our reference.
*
* Because entries can be reallocated to other neighbors once their ref count
* drops to 0 we need to take the entry's lock to avoid races with a new
* incarnation.
*/
static void _t4_l2e_free(struct l2t_entry *e)
{
struct l2t_data *d;
if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */
if (e->neigh) {
neigh_release(e->neigh);
e->neigh = NULL;
}
__skb_queue_purge(&e->arpq);
}
d = container_of(e, struct l2t_data, l2tab[e->idx]);
atomic_inc(&d->nfree);
}
/* Locked version of _t4_l2e_free */
static void t4_l2e_free(struct l2t_entry *e)
{
struct l2t_data *d;
spin_lock_bh(&e->lock);
if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */
if (e->neigh) {
neigh_release(e->neigh);
e->neigh = NULL;
}
__skb_queue_purge(&e->arpq);
}
spin_unlock_bh(&e->lock);
d = container_of(e, struct l2t_data, l2tab[e->idx]);
atomic_inc(&d->nfree);
}
void cxgb4_l2t_release(struct l2t_entry *e)
{
if (atomic_dec_and_test(&e->refcnt))
t4_l2e_free(e);
}
EXPORT_SYMBOL(cxgb4_l2t_release);
/*
* Update an L2T entry that was previously used for the same next hop as neigh.
* Must be called with softirqs disabled.
*/
static void reuse_entry(struct l2t_entry *e, struct neighbour *neigh)
{
unsigned int nud_state;
spin_lock(&e->lock); /* avoid race with t4_l2t_free */
if (neigh != e->neigh)
neigh_replace(e, neigh);
nud_state = neigh->nud_state;
if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac)) ||
!(nud_state & NUD_VALID))
e->state = L2T_STATE_RESOLVING;
else if (nud_state & NUD_CONNECTED)
e->state = L2T_STATE_VALID;
else
e->state = L2T_STATE_STALE;
spin_unlock(&e->lock);
}
struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh,
const struct net_device *physdev,
unsigned int priority)
{
u8 lport;
u16 vlan;
struct l2t_entry *e;
unsigned int addr_len = neigh->tbl->key_len;
u32 *addr = (u32 *)neigh->primary_key;
int ifidx = neigh->dev->ifindex;
int hash = addr_hash(d, addr, addr_len, ifidx);
if (neigh->dev->flags & IFF_LOOPBACK)
lport = netdev2pinfo(physdev)->tx_chan + 4;
else
lport = netdev2pinfo(physdev)->lport;
if (is_vlan_dev(neigh->dev)) {
vlan = vlan_dev_vlan_id(neigh->dev);
vlan |= vlan_dev_get_egress_qos_mask(neigh->dev, priority);
} else {
vlan = VLAN_NONE;
}
write_lock_bh(&d->lock);
for (e = d->l2tab[hash].first; e; e = e->next)
if (!addreq(e, addr) && e->ifindex == ifidx &&
e->vlan == vlan && e->lport == lport) {
l2t_hold(d, e);
if (atomic_read(&e->refcnt) == 1)
reuse_entry(e, neigh);
goto done;
}
/* Need to allocate a new entry */
e = alloc_l2e(d);
if (e) {
spin_lock(&e->lock); /* avoid race with t4_l2t_free */
e->state = L2T_STATE_RESOLVING;
if (neigh->dev->flags & IFF_LOOPBACK)
memcpy(e->dmac, physdev->dev_addr, sizeof(e->dmac));
memcpy(e->addr, addr, addr_len);
e->ifindex = ifidx;
e->hash = hash;
e->lport = lport;
e->v6 = addr_len == 16;
atomic_set(&e->refcnt, 1);
neigh_replace(e, neigh);
e->vlan = vlan;
e->next = d->l2tab[hash].first;
d->l2tab[hash].first = e;
spin_unlock(&e->lock);
}
done:
write_unlock_bh(&d->lock);
return e;
}
EXPORT_SYMBOL(cxgb4_l2t_get);
u64 cxgb4_select_ntuple(struct net_device *dev,
const struct l2t_entry *l2t)
{
struct adapter *adap = netdev2adap(dev);
struct tp_params *tp = &adap->params.tp;
u64 ntuple = 0;
/* Initialize each of the fields which we care about which are present
* in the Compressed Filter Tuple.
*/
if (tp->vlan_shift >= 0 && l2t->vlan != VLAN_NONE)
ntuple |= (u64)(FT_VLAN_VLD_F | l2t->vlan) << tp->vlan_shift;
if (tp->port_shift >= 0)
ntuple |= (u64)l2t->lport << tp->port_shift;
if (tp->protocol_shift >= 0)
ntuple |= (u64)IPPROTO_TCP << tp->protocol_shift;
if (tp->vnic_shift >= 0 && (tp->ingress_config & VNIC_F)) {
struct port_info *pi = (struct port_info *)netdev_priv(dev);
ntuple |= (u64)(FT_VNID_ID_VF_V(pi->vin) |
FT_VNID_ID_PF_V(adap->pf) |
FT_VNID_ID_VLD_V(pi->vivld)) << tp->vnic_shift;
}
return ntuple;
}
EXPORT_SYMBOL(cxgb4_select_ntuple);
/*
* Called when the host's neighbor layer makes a change to some entry that is
* loaded into the HW L2 table.
*/
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
{
unsigned int addr_len = neigh->tbl->key_len;
u32 *addr = (u32 *) neigh->primary_key;
int hash, ifidx = neigh->dev->ifindex;
struct sk_buff_head *arpq = NULL;
struct l2t_data *d = adap->l2t;
struct l2t_entry *e;
hash = addr_hash(d, addr, addr_len, ifidx);
read_lock_bh(&d->lock);
for (e = d->l2tab[hash].first; e; e = e->next)
if (!addreq(e, addr) && e->ifindex == ifidx) {
spin_lock(&e->lock);
if (atomic_read(&e->refcnt))
goto found;
spin_unlock(&e->lock);
break;
}
read_unlock_bh(&d->lock);
return;
found:
read_unlock(&d->lock);
if (neigh != e->neigh)
neigh_replace(e, neigh);
if (e->state == L2T_STATE_RESOLVING) {
if (neigh->nud_state & NUD_FAILED) {
arpq = &e->arpq;
} else if ((neigh->nud_state & (NUD_CONNECTED | NUD_STALE)) &&
!skb_queue_empty(&e->arpq)) {
write_l2e(adap, e, 1);
}
} else {
e->state = neigh->nud_state & NUD_CONNECTED ?
L2T_STATE_VALID : L2T_STATE_STALE;
if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac)))
write_l2e(adap, e, 0);
}
if (arpq) {
struct sk_buff *skb;
/* Called when address resolution fails for an L2T
* entry to handle packets on the arpq head. If a
* packet specifies a failure handler it is invoked,
* otherwise the packet is sent to the device.
*/
while ((skb = __skb_dequeue(&e->arpq)) != NULL) {
const struct l2t_skb_cb *cb = L2T_SKB_CB(skb);
spin_unlock(&e->lock);
if (cb->arp_err_handler)
cb->arp_err_handler(cb->handle, skb);
else
t4_ofld_send(adap, skb);
spin_lock(&e->lock);
}
}
spin_unlock_bh(&e->lock);
}
/* Allocate an L2T entry for use by a switching rule. Such need to be
* explicitly freed and while busy they are not on any hash chain, so normal
* address resolution updates do not see them.
*/
struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
u8 port, u8 *eth_addr)
{
struct l2t_data *d = adap->l2t;
struct l2t_entry *e;
int ret;
write_lock_bh(&d->lock);
e = find_or_alloc_l2e(d, vlan, port, eth_addr);
if (e) {
spin_lock(&e->lock); /* avoid race with t4_l2t_free */
if (!atomic_read(&e->refcnt)) {
e->state = L2T_STATE_SWITCHING;
e->vlan = vlan;
e->lport = port;
ether_addr_copy(e->dmac, eth_addr);
atomic_set(&e->refcnt, 1);
ret = write_l2e(adap, e, 0);
if (ret < 0) {
_t4_l2e_free(e);
spin_unlock(&e->lock);
write_unlock_bh(&d->lock);
return NULL;
}
} else {
atomic_inc(&e->refcnt);
}
spin_unlock(&e->lock);
}
write_unlock_bh(&d->lock);
return e;
}
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end)
{
unsigned int l2t_size;
int i;
struct l2t_data *d;
if (l2t_start >= l2t_end || l2t_end >= L2T_SIZE)
return NULL;
l2t_size = l2t_end - l2t_start + 1;
if (l2t_size < L2T_MIN_HASH_BUCKETS)
return NULL;
d = kvzalloc_flex(*d, l2tab, l2t_size);
if (!d)
return NULL;
d->l2t_start = l2t_start;
d->l2t_size = l2t_size;
d->rover = d->l2tab;
atomic_set(&d->nfree, l2t_size);
rwlock_init(&d->lock);
for (i = 0; i < d->l2t_size; ++i) {
d->l2tab[i].idx = i;
d->l2tab[i].state = L2T_STATE_UNUSED;
spin_lock_init(&d->l2tab[i].lock);
atomic_set(&d->l2tab[i].refcnt, 0);
skb_queue_head_init(&d->l2tab[i].arpq);
}
return d;
}
static inline void *l2t_get_idx(struct seq_file *seq, loff_t pos)
{
struct l2t_data *d = seq->private;
return pos >= d->l2t_size ? NULL : &d->l2tab[pos];
}
static void *l2t_seq_start(struct seq_file *seq, loff_t *pos)
{
return *pos ? l2t_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
}
static void *l2t_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
v = l2t_get_idx(seq, *pos);
++(*pos);
return v;
}
static void l2t_seq_stop(struct seq_file *seq, void *v)
{
}
static char l2e_state(const struct l2t_entry *e)
{
switch (e->state) {
case L2T_STATE_VALID: return 'V';
case L2T_STATE_STALE: return 'S';
case L2T_STATE_SYNC_WRITE: return 'W';
case L2T_STATE_RESOLVING:
return skb_queue_empty(&e->arpq) ? 'R' : 'A';
case L2T_STATE_SWITCHING: return 'X';
default:
return 'U';
}
}
bool cxgb4_check_l2t_valid(struct l2t_entry *e)
{
bool valid;
spin_lock(&e->lock);
valid = (e->state == L2T_STATE_VALID);
spin_unlock(&e->lock);
return valid;
}
EXPORT_SYMBOL(cxgb4_check_l2t_valid);
static int l2t_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq, " Idx IP address "
"Ethernet address VLAN/P LP State Users Port\n");
else {
char ip[60];
struct l2t_data *d = seq->private;
struct l2t_entry *e = v;
spin_lock_bh(&e->lock);
if (e->state == L2T_STATE_SWITCHING)
ip[0] = '\0';
else
sprintf(ip, e->v6 ? "%pI6c" : "%pI4", e->addr);
seq_printf(seq, "%4u %-25s %17pM %4d %u %2u %c %5u %s\n",
e->idx + d->l2t_start, ip, e->dmac,
e->vlan & VLAN_VID_MASK, vlan_prio(e), e->lport,
l2e_state(e), atomic_read(&e->refcnt),
e->neigh ? e->neigh->dev->name : "");
spin_unlock_bh(&e->lock);
}
return 0;
}
static const struct seq_operations l2t_seq_ops = {
.start = l2t_seq_start,
.next = l2t_seq_next,
.stop = l2t_seq_stop,
.show = l2t_seq_show
};
static int l2t_seq_open(struct inode *inode, struct file *file)
{
int rc = seq_open(file, &l2t_seq_ops);
if (!rc) {
struct adapter *adap = inode->i_private;
struct seq_file *seq = file->private_data;
seq->private = adap->l2t;
}
return rc;
}
const struct file_operations t4_l2t_fops = {
.owner = THIS_MODULE,
.open = l2t_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
@@ -0,0 +1,126 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_L2T_H
#define __CXGB4_L2T_H
#include <linux/spinlock.h>
#include <linux/if_ether.h>
#include <linux/atomic.h>
#define VLAN_NONE 0xfff
enum { L2T_SIZE = 4096 }; /* # of L2T entries */
enum {
L2T_STATE_VALID, /* entry is up to date */
L2T_STATE_STALE, /* entry may be used but needs revalidation */
L2T_STATE_RESOLVING, /* entry needs address resolution */
L2T_STATE_SYNC_WRITE, /* synchronous write of entry underway */
L2T_STATE_NOARP, /* Netdev down or removed*/
/* when state is one of the below the entry is not hashed */
L2T_STATE_SWITCHING, /* entry is being used by a switching filter */
L2T_STATE_UNUSED /* entry not in use */
};
struct adapter;
struct l2t_data;
struct neighbour;
struct net_device;
struct file_operations;
struct cpl_l2t_write_rpl;
/*
* Each L2T entry plays multiple roles. First of all, it keeps state for the
* corresponding entry of the HW L2 table and maintains a queue of offload
* packets awaiting address resolution. Second, it is a node of a hash table
* chain, where the nodes of the chain are linked together through their next
* pointer. Finally, each node is a bucket of a hash table, pointing to the
* first element in its chain through its first pointer.
*/
struct l2t_entry {
u16 state; /* entry state */
u16 idx; /* entry index within in-memory table */
u32 addr[4]; /* next hop IP or IPv6 address */
int ifindex; /* neighbor's net_device's ifindex */
struct neighbour *neigh; /* associated neighbour */
struct l2t_entry *first; /* start of hash chain */
struct l2t_entry *next; /* next l2t_entry on chain */
struct sk_buff_head arpq; /* packet queue awaiting resolution */
spinlock_t lock;
atomic_t refcnt; /* entry reference count */
u16 hash; /* hash bucket the entry is on */
u16 vlan; /* VLAN TCI (id: bits 0-11, prio: 13-15 */
u8 v6; /* whether entry is for IPv6 */
u8 lport; /* associated offload logical interface */
u8 dmac[ETH_ALEN]; /* neighbour's MAC address */
};
typedef void (*arp_err_handler_t)(void *handle, struct sk_buff *skb);
/*
* Callback stored in an skb to handle address resolution failure.
*/
struct l2t_skb_cb {
void *handle;
arp_err_handler_t arp_err_handler;
};
#define L2T_SKB_CB(skb) ((struct l2t_skb_cb *)(skb)->cb)
static inline void t4_set_arp_err_handler(struct sk_buff *skb, void *handle,
arp_err_handler_t handler)
{
L2T_SKB_CB(skb)->handle = handle;
L2T_SKB_CB(skb)->arp_err_handler = handler;
}
void cxgb4_l2t_release(struct l2t_entry *e);
int cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb,
struct l2t_entry *e);
struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh,
const struct net_device *physdev,
unsigned int priority);
u64 cxgb4_select_ntuple(struct net_device *dev,
const struct l2t_entry *l2t);
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh);
struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
u8 port, u8 *dmac);
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end);
void do_l2t_write_rpl(struct adapter *p, const struct cpl_l2t_write_rpl *rpl);
bool cxgb4_check_l2t_valid(struct l2t_entry *e);
extern const struct file_operations t4_l2t_fops;
#endif /* __CXGB4_L2T_H */
@@ -0,0 +1,693 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/module.h>
#include <linux/netdevice.h>
#include "cxgb4.h"
#include "sched.h"
static int t4_sched_class_fw_cmd(struct port_info *pi,
struct ch_sched_params *p,
enum sched_fw_ops op)
{
struct adapter *adap = pi->adapter;
struct sched_table *s = pi->sched_tbl;
struct ch_sched_class *e;
int err = 0;
e = &s->tab[p->u.params.class];
switch (op) {
case SCHED_FW_OP_ADD:
case SCHED_FW_OP_DEL:
err = t4_sched_params(adap, p->type,
p->u.params.level, p->u.params.mode,
p->u.params.rateunit,
p->u.params.ratemode,
p->u.params.channel, e->idx,
p->u.params.minrate, p->u.params.maxrate,
p->u.params.weight, p->u.params.pktsize,
p->u.params.burstsize);
break;
default:
err = -ENOTSUPP;
break;
}
return err;
}
static int t4_sched_bind_unbind_op(struct port_info *pi, void *arg,
enum sched_bind_type type, bool bind)
{
struct adapter *adap = pi->adapter;
u32 fw_mnem, fw_class, fw_param;
unsigned int pf = adap->pf;
unsigned int vf = 0;
int err = 0;
switch (type) {
case SCHED_QUEUE: {
struct sched_queue_entry *qe;
qe = (struct sched_queue_entry *)arg;
/* Create a template for the FW_PARAMS_CMD mnemonic and
* value (TX Scheduling Class in this case).
*/
fw_mnem = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X_V(
FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH));
fw_class = bind ? qe->param.class : FW_SCHED_CLS_NONE;
fw_param = (fw_mnem | FW_PARAMS_PARAM_YZ_V(qe->cntxt_id));
pf = adap->pf;
vf = 0;
err = t4_set_params(adap, adap->mbox, pf, vf, 1,
&fw_param, &fw_class);
break;
}
case SCHED_FLOWC: {
struct sched_flowc_entry *fe;
fe = (struct sched_flowc_entry *)arg;
fw_class = bind ? fe->param.class : FW_SCHED_CLS_NONE;
err = cxgb4_ethofld_send_flowc(adap->port[pi->port_id],
fe->param.tid, fw_class);
break;
}
default:
err = -ENOTSUPP;
break;
}
return err;
}
static void *t4_sched_entry_lookup(struct port_info *pi,
enum sched_bind_type type,
const u32 val)
{
struct sched_table *s = pi->sched_tbl;
struct ch_sched_class *e, *end;
void *found = NULL;
/* Look for an entry with matching @val */
end = &s->tab[s->sched_size];
for (e = &s->tab[0]; e != end; ++e) {
if (e->state == SCHED_STATE_UNUSED ||
e->bind_type != type)
continue;
switch (type) {
case SCHED_QUEUE: {
struct sched_queue_entry *qe;
list_for_each_entry(qe, &e->entry_list, list) {
if (qe->cntxt_id == val) {
found = qe;
break;
}
}
break;
}
case SCHED_FLOWC: {
struct sched_flowc_entry *fe;
list_for_each_entry(fe, &e->entry_list, list) {
if (fe->param.tid == val) {
found = fe;
break;
}
}
break;
}
default:
return NULL;
}
if (found)
break;
}
return found;
}
struct ch_sched_class *cxgb4_sched_queue_lookup(struct net_device *dev,
struct ch_sched_queue *p)
{
struct port_info *pi = netdev2pinfo(dev);
struct sched_queue_entry *qe = NULL;
struct adapter *adap = pi->adapter;
struct sge_eth_txq *txq;
if (p->queue < 0 || p->queue >= pi->nqsets)
return NULL;
txq = &adap->sge.ethtxq[pi->first_qset + p->queue];
qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id);
return qe ? &pi->sched_tbl->tab[qe->param.class] : NULL;
}
static int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p)
{
struct sched_queue_entry *qe = NULL;
struct adapter *adap = pi->adapter;
struct sge_eth_txq *txq;
struct ch_sched_class *e;
int err = 0;
if (p->queue < 0 || p->queue >= pi->nqsets)
return -ERANGE;
txq = &adap->sge.ethtxq[pi->first_qset + p->queue];
/* Find the existing entry that the queue is bound to */
qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id);
if (qe) {
err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE,
false);
if (err)
return err;
e = &pi->sched_tbl->tab[qe->param.class];
list_del(&qe->list);
kvfree(qe);
if (atomic_dec_and_test(&e->refcnt))
cxgb4_sched_class_free(adap->port[pi->port_id], e->idx);
}
return err;
}
static int t4_sched_queue_bind(struct port_info *pi, struct ch_sched_queue *p)
{
struct sched_table *s = pi->sched_tbl;
struct sched_queue_entry *qe = NULL;
struct adapter *adap = pi->adapter;
struct sge_eth_txq *txq;
struct ch_sched_class *e;
unsigned int qid;
int err = 0;
if (p->queue < 0 || p->queue >= pi->nqsets)
return -ERANGE;
qe = kvzalloc_obj(struct sched_queue_entry);
if (!qe)
return -ENOMEM;
txq = &adap->sge.ethtxq[pi->first_qset + p->queue];
qid = txq->q.cntxt_id;
/* Unbind queue from any existing class */
err = t4_sched_queue_unbind(pi, p);
if (err)
goto out_err;
/* Bind queue to specified class */
qe->cntxt_id = qid;
memcpy(&qe->param, p, sizeof(qe->param));
e = &s->tab[qe->param.class];
err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, true);
if (err)
goto out_err;
list_add_tail(&qe->list, &e->entry_list);
e->bind_type = SCHED_QUEUE;
atomic_inc(&e->refcnt);
return err;
out_err:
kvfree(qe);
return err;
}
static int t4_sched_flowc_unbind(struct port_info *pi, struct ch_sched_flowc *p)
{
struct sched_flowc_entry *fe = NULL;
struct adapter *adap = pi->adapter;
struct ch_sched_class *e;
int err = 0;
if (p->tid < 0 || p->tid >= adap->tids.neotids)
return -ERANGE;
/* Find the existing entry that the flowc is bound to */
fe = t4_sched_entry_lookup(pi, SCHED_FLOWC, p->tid);
if (fe) {
err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC,
false);
if (err)
return err;
e = &pi->sched_tbl->tab[fe->param.class];
list_del(&fe->list);
kvfree(fe);
if (atomic_dec_and_test(&e->refcnt))
cxgb4_sched_class_free(adap->port[pi->port_id], e->idx);
}
return err;
}
static int t4_sched_flowc_bind(struct port_info *pi, struct ch_sched_flowc *p)
{
struct sched_table *s = pi->sched_tbl;
struct sched_flowc_entry *fe = NULL;
struct adapter *adap = pi->adapter;
struct ch_sched_class *e;
int err = 0;
if (p->tid < 0 || p->tid >= adap->tids.neotids)
return -ERANGE;
fe = kvzalloc_obj(*fe);
if (!fe)
return -ENOMEM;
/* Unbind flowc from any existing class */
err = t4_sched_flowc_unbind(pi, p);
if (err)
goto out_err;
/* Bind flowc to specified class */
memcpy(&fe->param, p, sizeof(fe->param));
e = &s->tab[fe->param.class];
err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC, true);
if (err)
goto out_err;
list_add_tail(&fe->list, &e->entry_list);
e->bind_type = SCHED_FLOWC;
atomic_inc(&e->refcnt);
return err;
out_err:
kvfree(fe);
return err;
}
static void t4_sched_class_unbind_all(struct port_info *pi,
struct ch_sched_class *e,
enum sched_bind_type type)
{
if (!e)
return;
switch (type) {
case SCHED_QUEUE: {
struct sched_queue_entry *qe;
list_for_each_entry(qe, &e->entry_list, list)
t4_sched_queue_unbind(pi, &qe->param);
break;
}
case SCHED_FLOWC: {
struct sched_flowc_entry *fe;
list_for_each_entry(fe, &e->entry_list, list)
t4_sched_flowc_unbind(pi, &fe->param);
break;
}
default:
break;
}
}
static int t4_sched_class_bind_unbind_op(struct port_info *pi, void *arg,
enum sched_bind_type type, bool bind)
{
int err = 0;
if (!arg)
return -EINVAL;
switch (type) {
case SCHED_QUEUE: {
struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;
if (bind)
err = t4_sched_queue_bind(pi, qe);
else
err = t4_sched_queue_unbind(pi, qe);
break;
}
case SCHED_FLOWC: {
struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg;
if (bind)
err = t4_sched_flowc_bind(pi, fe);
else
err = t4_sched_flowc_unbind(pi, fe);
break;
}
default:
err = -ENOTSUPP;
break;
}
return err;
}
/**
* cxgb4_sched_class_bind - Bind an entity to a scheduling class
* @dev: net_device pointer
* @arg: Entity opaque data
* @type: Entity type (Queue)
*
* Binds an entity (queue) to a scheduling class. If the entity
* is bound to another class, it will be unbound from the other class
* and bound to the class specified in @arg.
*/
int cxgb4_sched_class_bind(struct net_device *dev, void *arg,
enum sched_bind_type type)
{
struct port_info *pi = netdev2pinfo(dev);
u8 class_id;
if (!can_sched(dev))
return -ENOTSUPP;
if (!arg)
return -EINVAL;
switch (type) {
case SCHED_QUEUE: {
struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;
class_id = qe->class;
break;
}
case SCHED_FLOWC: {
struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg;
class_id = fe->class;
break;
}
default:
return -ENOTSUPP;
}
if (!valid_class_id(dev, class_id))
return -EINVAL;
if (class_id == SCHED_CLS_NONE)
return -ENOTSUPP;
return t4_sched_class_bind_unbind_op(pi, arg, type, true);
}
/**
* cxgb4_sched_class_unbind - Unbind an entity from a scheduling class
* @dev: net_device pointer
* @arg: Entity opaque data
* @type: Entity type (Queue)
*
* Unbinds an entity (queue) from a scheduling class.
*/
int cxgb4_sched_class_unbind(struct net_device *dev, void *arg,
enum sched_bind_type type)
{
struct port_info *pi = netdev2pinfo(dev);
u8 class_id;
if (!can_sched(dev))
return -ENOTSUPP;
if (!arg)
return -EINVAL;
switch (type) {
case SCHED_QUEUE: {
struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;
class_id = qe->class;
break;
}
case SCHED_FLOWC: {
struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg;
class_id = fe->class;
break;
}
default:
return -ENOTSUPP;
}
if (!valid_class_id(dev, class_id))
return -EINVAL;
return t4_sched_class_bind_unbind_op(pi, arg, type, false);
}
/* If @p is NULL, fetch any available unused class */
static struct ch_sched_class *t4_sched_class_lookup(struct port_info *pi,
const struct ch_sched_params *p)
{
struct sched_table *s = pi->sched_tbl;
struct ch_sched_class *found = NULL;
struct ch_sched_class *e, *end;
if (!p) {
/* Get any available unused class */
end = &s->tab[s->sched_size];
for (e = &s->tab[0]; e != end; ++e) {
if (e->state == SCHED_STATE_UNUSED) {
found = e;
break;
}
}
} else {
/* Look for a class with matching scheduling parameters */
struct ch_sched_params info;
struct ch_sched_params tp;
memcpy(&tp, p, sizeof(tp));
/* Don't try to match class parameter */
tp.u.params.class = SCHED_CLS_NONE;
end = &s->tab[s->sched_size];
for (e = &s->tab[0]; e != end; ++e) {
if (e->state == SCHED_STATE_UNUSED)
continue;
memcpy(&info, &e->info, sizeof(info));
/* Don't try to match class parameter */
info.u.params.class = SCHED_CLS_NONE;
if ((info.type == tp.type) &&
(!memcmp(&info.u.params, &tp.u.params,
sizeof(info.u.params)))) {
found = e;
break;
}
}
}
return found;
}
static struct ch_sched_class *t4_sched_class_alloc(struct port_info *pi,
struct ch_sched_params *p)
{
struct ch_sched_class *e = NULL;
u8 class_id;
int err;
if (!p)
return NULL;
class_id = p->u.params.class;
/* Only accept search for existing class with matching params
* or allocation of new class with specified params
*/
if (class_id != SCHED_CLS_NONE)
return NULL;
/* See if there's an exisiting class with same requested sched
* params. Classes can only be shared among FLOWC types. For
* other types, always request a new class.
*/
if (p->u.params.mode == SCHED_CLASS_MODE_FLOW)
e = t4_sched_class_lookup(pi, p);
if (!e) {
struct ch_sched_params np;
/* Fetch any available unused class */
e = t4_sched_class_lookup(pi, NULL);
if (!e)
return NULL;
memcpy(&np, p, sizeof(np));
np.u.params.class = e->idx;
/* New class */
err = t4_sched_class_fw_cmd(pi, &np, SCHED_FW_OP_ADD);
if (err)
return NULL;
memcpy(&e->info, &np, sizeof(e->info));
atomic_set(&e->refcnt, 0);
e->state = SCHED_STATE_ACTIVE;
}
return e;
}
/**
* cxgb4_sched_class_alloc - allocate a scheduling class
* @dev: net_device pointer
* @p: new scheduling class to create.
*
* Returns pointer to the scheduling class created. If @p is NULL, then
* it allocates and returns any available unused scheduling class. If a
* scheduling class with matching @p is found, then the matching class is
* returned.
*/
struct ch_sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
struct ch_sched_params *p)
{
struct port_info *pi = netdev2pinfo(dev);
u8 class_id;
if (!can_sched(dev))
return NULL;
class_id = p->u.params.class;
if (!valid_class_id(dev, class_id))
return NULL;
return t4_sched_class_alloc(pi, p);
}
/**
* cxgb4_sched_class_free - free a scheduling class
* @dev: net_device pointer
* @classid: scheduling class id to free
*
* Frees a scheduling class if there are no users.
*/
void cxgb4_sched_class_free(struct net_device *dev, u8 classid)
{
struct port_info *pi = netdev2pinfo(dev);
struct sched_table *s = pi->sched_tbl;
struct ch_sched_params p;
struct ch_sched_class *e;
u32 speed;
int ret;
e = &s->tab[classid];
if (!atomic_read(&e->refcnt) && e->state != SCHED_STATE_UNUSED) {
/* Port based rate limiting needs explicit reset back
* to max rate. But, we'll do explicit reset for all
* types, instead of just port based type, to be on
* the safer side.
*/
memcpy(&p, &e->info, sizeof(p));
/* Always reset mode to 0. Otherwise, FLOWC mode will
* still be enabled even after resetting the traffic
* class.
*/
p.u.params.mode = 0;
p.u.params.minrate = 0;
p.u.params.pktsize = 0;
ret = t4_get_link_params(pi, NULL, &speed, NULL);
if (!ret)
p.u.params.maxrate = speed * 1000; /* Mbps to Kbps */
else
p.u.params.maxrate = SCHED_MAX_RATE_KBPS;
t4_sched_class_fw_cmd(pi, &p, SCHED_FW_OP_DEL);
e->state = SCHED_STATE_UNUSED;
memset(&e->info, 0, sizeof(e->info));
}
}
static void t4_sched_class_free(struct net_device *dev, struct ch_sched_class *e)
{
struct port_info *pi = netdev2pinfo(dev);
t4_sched_class_unbind_all(pi, e, e->bind_type);
cxgb4_sched_class_free(dev, e->idx);
}
struct sched_table *t4_init_sched(unsigned int sched_size)
{
struct sched_table *s;
unsigned int i;
s = kvzalloc_flex(*s, tab, sched_size);
if (!s)
return NULL;
s->sched_size = sched_size;
for (i = 0; i < s->sched_size; i++) {
memset(&s->tab[i], 0, sizeof(struct ch_sched_class));
s->tab[i].idx = i;
s->tab[i].state = SCHED_STATE_UNUSED;
INIT_LIST_HEAD(&s->tab[i].entry_list);
atomic_set(&s->tab[i].refcnt, 0);
}
return s;
}
void t4_cleanup_sched(struct adapter *adap)
{
struct sched_table *s;
unsigned int j, i;
for_each_port(adap, j) {
struct port_info *pi = netdev2pinfo(adap->port[j]);
s = pi->sched_tbl;
if (!s)
continue;
for (i = 0; i < s->sched_size; i++) {
struct ch_sched_class *e;
e = &s->tab[i];
if (e->state == SCHED_STATE_ACTIVE)
t4_sched_class_free(adap->port[j], e);
}
kvfree(s);
}
}
@@ -0,0 +1,119 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_SCHED_H
#define __CXGB4_SCHED_H
#include <linux/spinlock.h>
#include <linux/atomic.h>
#define SCHED_CLS_NONE 0xff
#define FW_SCHED_CLS_NONE 0xffffffff
/* Max rate that can be set to a scheduling class is 100 Gbps */
#define SCHED_MAX_RATE_KBPS 100000000U
enum {
SCHED_STATE_ACTIVE,
SCHED_STATE_UNUSED,
};
enum sched_fw_ops {
SCHED_FW_OP_ADD,
SCHED_FW_OP_DEL,
};
enum sched_bind_type {
SCHED_QUEUE,
SCHED_FLOWC,
};
struct sched_queue_entry {
struct list_head list;
unsigned int cntxt_id;
struct ch_sched_queue param;
};
struct sched_flowc_entry {
struct list_head list;
struct ch_sched_flowc param;
};
struct ch_sched_class {
u8 state;
u8 idx;
struct ch_sched_params info;
enum sched_bind_type bind_type;
struct list_head entry_list;
atomic_t refcnt;
};
struct sched_table { /* per port scheduling table */
u8 sched_size;
struct ch_sched_class tab[] __counted_by(sched_size);
};
static inline bool can_sched(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
return !pi->sched_tbl ? false : true;
}
static inline bool valid_class_id(struct net_device *dev, u8 class_id)
{
struct port_info *pi = netdev2pinfo(dev);
if ((class_id > pi->sched_tbl->sched_size - 1) &&
(class_id != SCHED_CLS_NONE))
return false;
return true;
}
struct ch_sched_class *cxgb4_sched_queue_lookup(struct net_device *dev,
struct ch_sched_queue *p);
int cxgb4_sched_class_bind(struct net_device *dev, void *arg,
enum sched_bind_type type);
int cxgb4_sched_class_unbind(struct net_device *dev, void *arg,
enum sched_bind_type type);
struct ch_sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
struct ch_sched_params *p);
void cxgb4_sched_class_free(struct net_device *dev, u8 classid);
struct sched_table *t4_init_sched(unsigned int size);
void t4_cleanup_sched(struct adapter *adap);
#endif /* __CXGB4_SCHED_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,248 @@
/*
* This file is part of the Chelsio T4/T5/T6 Ethernet driver for Linux.
*
* Copyright (c) 2017 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "cxgb4.h"
#include "smt.h"
#include "t4_msg.h"
#include "t4fw_api.h"
#include "t4_regs.h"
#include "t4_values.h"
struct smt_data *t4_init_smt(void)
{
unsigned int smt_size;
struct smt_data *s;
int i;
smt_size = SMT_SIZE;
s = kvzalloc_flex(*s, smtab, smt_size);
if (!s)
return NULL;
s->smt_size = smt_size;
rwlock_init(&s->lock);
for (i = 0; i < s->smt_size; ++i) {
s->smtab[i].idx = i;
s->smtab[i].state = SMT_STATE_UNUSED;
eth_zero_addr(s->smtab[i].src_mac);
spin_lock_init(&s->smtab[i].lock);
s->smtab[i].refcnt = 0;
}
return s;
}
static struct smt_entry *find_or_alloc_smte(struct smt_data *s, u8 *smac)
{
struct smt_entry *first_free = NULL;
struct smt_entry *e, *end;
for (e = &s->smtab[0], end = &s->smtab[s->smt_size]; e != end; ++e) {
if (e->refcnt == 0) {
if (!first_free)
first_free = e;
} else {
if (e->state == SMT_STATE_SWITCHING) {
/* This entry is actually in use. See if we can
* re-use it ?
*/
if (memcmp(e->src_mac, smac, ETH_ALEN) == 0)
goto found_reuse;
}
}
}
if (first_free) {
e = first_free;
goto found;
}
return NULL;
found:
e->state = SMT_STATE_UNUSED;
found_reuse:
return e;
}
static void t4_smte_free(struct smt_entry *e)
{
if (e->refcnt == 0) { /* hasn't been recycled */
e->state = SMT_STATE_UNUSED;
}
}
/**
* cxgb4_smt_release - Release SMT entry
* @e: smt entry to release
*
* Releases ref count and frees up an smt entry from SMT table
*/
void cxgb4_smt_release(struct smt_entry *e)
{
spin_lock_bh(&e->lock);
if ((--e->refcnt) == 0)
t4_smte_free(e);
spin_unlock_bh(&e->lock);
}
EXPORT_SYMBOL(cxgb4_smt_release);
void do_smt_write_rpl(struct adapter *adap, const struct cpl_smt_write_rpl *rpl)
{
unsigned int smtidx = TID_TID_G(GET_TID(rpl));
struct smt_data *s = adap->smt;
if (unlikely(rpl->status != CPL_ERR_NONE)) {
struct smt_entry *e = &s->smtab[smtidx];
dev_err(adap->pdev_dev,
"Unexpected SMT_WRITE_RPL status %u for entry %u\n",
rpl->status, smtidx);
spin_lock(&e->lock);
e->state = SMT_STATE_ERROR;
spin_unlock(&e->lock);
return;
}
}
static int write_smt_entry(struct adapter *adapter, struct smt_entry *e)
{
struct cpl_t6_smt_write_req *t6req;
struct smt_data *s = adapter->smt;
struct cpl_smt_write_req *req;
struct sk_buff *skb;
int size;
u8 row;
if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) {
size = sizeof(*req);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
/* Source MAC Table (SMT) contains 256 SMAC entries
* organized in 128 rows of 2 entries each.
*/
req = (struct cpl_smt_write_req *)__skb_put(skb, size);
INIT_TP_WR(req, 0);
/* Each row contains an SMAC pair.
* LSB selects the SMAC entry within a row
*/
row = (e->idx >> 1);
if (e->idx & 1) {
req->pfvf1 = 0x0;
memcpy(req->src_mac1, e->src_mac, ETH_ALEN);
/* fill pfvf0/src_mac0 with entry
* at prev index from smt-tab.
*/
req->pfvf0 = 0x0;
memcpy(req->src_mac0, s->smtab[e->idx - 1].src_mac,
ETH_ALEN);
} else {
req->pfvf0 = 0x0;
memcpy(req->src_mac0, e->src_mac, ETH_ALEN);
/* fill pfvf1/src_mac1 with entry
* at next index from smt-tab
*/
req->pfvf1 = 0x0;
memcpy(req->src_mac1, s->smtab[e->idx + 1].src_mac,
ETH_ALEN);
}
} else {
size = sizeof(*t6req);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
/* Source MAC Table (SMT) contains 256 SMAC entries */
t6req = (struct cpl_t6_smt_write_req *)__skb_put(skb, size);
INIT_TP_WR(t6req, 0);
req = (struct cpl_smt_write_req *)t6req;
/* fill pfvf0/src_mac0 from smt-tab */
req->pfvf0 = 0x0;
memcpy(req->src_mac0, s->smtab[e->idx].src_mac, ETH_ALEN);
row = e->idx;
}
OPCODE_TID(req) =
htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, e->idx |
TID_QID_V(adapter->sge.fw_evtq.abs_id)));
req->params = htonl(SMTW_NORPL_V(0) |
SMTW_IDX_V(row) |
SMTW_OVLAN_IDX_V(0));
t4_mgmt_tx(adapter, skb);
return 0;
}
static struct smt_entry *t4_smt_alloc_switching(struct adapter *adap, u16 pfvf,
u8 *smac)
{
struct smt_data *s = adap->smt;
struct smt_entry *e;
write_lock_bh(&s->lock);
e = find_or_alloc_smte(s, smac);
if (e) {
spin_lock(&e->lock);
if (!e->refcnt) {
e->refcnt = 1;
e->state = SMT_STATE_SWITCHING;
e->pfvf = pfvf;
memcpy(e->src_mac, smac, ETH_ALEN);
write_smt_entry(adap, e);
} else {
++e->refcnt;
}
spin_unlock(&e->lock);
}
write_unlock_bh(&s->lock);
return e;
}
/**
* cxgb4_smt_alloc_switching - Allocates an SMT entry for switch filters.
* @dev: net_device pointer
* @smac: MAC address to add to SMT
* Returns pointer to the SMT entry created
*
* Allocates an SMT entry to be used by switching rule of a filter.
*/
struct smt_entry *cxgb4_smt_alloc_switching(struct net_device *dev, u8 *smac)
{
struct adapter *adap = netdev2adap(dev);
return t4_smt_alloc_switching(adap, 0x0, smac);
}
EXPORT_SYMBOL(cxgb4_smt_alloc_switching);
@@ -0,0 +1,76 @@
/*
* This file is part of the Chelsio T4/T5/T6 Ethernet driver for Linux.
*
* Copyright (c) 2017 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_SMT_H
#define __CXGB4_SMT_H
#include <linux/spinlock.h>
#include <linux/if_ether.h>
#include <linux/atomic.h>
struct adapter;
struct cpl_smt_write_rpl;
/* SMT related handling. Heavily adapted based on l2t ops in l2t.h/l2t.c
*/
enum {
SMT_STATE_SWITCHING,
SMT_STATE_UNUSED,
SMT_STATE_ERROR
};
enum {
SMT_SIZE = 256
};
struct smt_entry {
u16 state;
u16 idx;
u16 pfvf;
u8 src_mac[ETH_ALEN];
int refcnt;
spinlock_t lock; /* protect smt entry add,removal */
};
struct smt_data {
unsigned int smt_size;
rwlock_t lock;
struct smt_entry smtab[] __counted_by(smt_size);
};
struct smt_data *t4_init_smt(void);
struct smt_entry *cxgb4_smt_alloc_switching(struct net_device *dev, u8 *smac);
void cxgb4_smt_release(struct smt_entry *e);
void do_smt_write_rpl(struct adapter *p, const struct cpl_smt_write_rpl *rpl);
#endif /* __CXGB4_SMT_H */
@@ -0,0 +1,79 @@
/*
* This file is part of the Chelsio T6 Ethernet driver for Linux.
*
* Copyright (c) 2017-2018 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "cxgb4.h"
#include "t4_msg.h"
#include "srq.h"
struct srq_data *t4_init_srq(int srq_size)
{
struct srq_data *s;
s = kvzalloc_obj(*s);
if (!s)
return NULL;
s->srq_size = srq_size;
init_completion(&s->comp);
mutex_init(&s->lock);
return s;
}
void do_srq_table_rpl(struct adapter *adap,
const struct cpl_srq_table_rpl *rpl)
{
unsigned int idx = TID_TID_G(GET_TID(rpl));
struct srq_data *s = adap->srq;
struct srq_entry *e;
if (unlikely(rpl->status != CPL_CONTAINS_READ_RPL)) {
dev_err(adap->pdev_dev,
"Unexpected SRQ_TABLE_RPL status %u for entry %u\n",
rpl->status, idx);
goto out;
}
/* Store the read entry */
e = s->entryp;
e->valid = 1;
e->idx = idx;
e->pdid = SRQT_PDID_G(be64_to_cpu(rpl->rsvd_pdid));
e->qlen = SRQT_QLEN_G(be32_to_cpu(rpl->qlen_qbase));
e->qbase = SRQT_QBASE_G(be32_to_cpu(rpl->qlen_qbase));
e->cur_msn = be16_to_cpu(rpl->cur_msn);
e->max_msn = be16_to_cpu(rpl->max_msn);
out:
complete(&s->comp);
}
@@ -0,0 +1,63 @@
/*
* This file is part of the Chelsio T6 Ethernet driver for Linux.
*
* Copyright (c) 2017-2018 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __CXGB4_SRQ_H
#define __CXGB4_SRQ_H
struct adapter;
struct cpl_srq_table_rpl;
#define SRQ_WAIT_TO (HZ * 5)
struct srq_entry {
u8 valid;
u8 idx;
u8 qlen;
u16 pdid;
u16 cur_msn;
u16 max_msn;
u32 qbase;
};
struct srq_data {
unsigned int srq_size;
struct srq_entry *entryp;
struct completion comp;
struct mutex lock; /* generic mutex for srq data */
};
struct srq_data *t4_init_srq(int srq_size);
void do_srq_table_rpl(struct adapter *adap,
const struct cpl_srq_table_rpl *rpl);
#endif /* __CXGB4_SRQ_H */
@@ -0,0 +1,87 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2015 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __T4_CHIP_TYPE_H__
#define __T4_CHIP_TYPE_H__
#define CHELSIO_PCI_ID_VER(__DeviceID) ((__DeviceID) >> 12)
#define CHELSIO_T4 0x4
#define CHELSIO_T5 0x5
#define CHELSIO_T6 0x6
/* We code the Chelsio T4 Family "Chip Code" as a tuple:
*
* (Chip Version, Chip Revision)
*
* where:
*
* Chip Version: is T4, T5, etc.
* Chip Revision: is the FAB "spin" of the Chip Version.
*/
#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision))
#define CHELSIO_CHIP_VERSION(code) (((code) >> 4) & 0xf)
#define CHELSIO_CHIP_RELEASE(code) ((code) & 0xf)
enum chip_type {
T4_A1 = CHELSIO_CHIP_CODE(CHELSIO_T4, 1),
T4_A2 = CHELSIO_CHIP_CODE(CHELSIO_T4, 2),
T4_FIRST_REV = T4_A1,
T4_LAST_REV = T4_A2,
T5_A0 = CHELSIO_CHIP_CODE(CHELSIO_T5, 0),
T5_A1 = CHELSIO_CHIP_CODE(CHELSIO_T5, 1),
T5_FIRST_REV = T5_A0,
T5_LAST_REV = T5_A1,
T6_A0 = CHELSIO_CHIP_CODE(CHELSIO_T6, 0),
T6_FIRST_REV = T6_A0,
T6_LAST_REV = T6_A0,
};
static inline int is_t4(enum chip_type chip)
{
return (CHELSIO_CHIP_VERSION(chip) == CHELSIO_T4);
}
static inline int is_t5(enum chip_type chip)
{
return (CHELSIO_CHIP_VERSION(chip) == CHELSIO_T5);
}
static inline int is_t6(enum chip_type chip)
{
return (CHELSIO_CHIP_VERSION(chip) == CHELSIO_T6);
}
#endif /* __T4_CHIP_TYPE_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,303 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __T4_HW_H
#define __T4_HW_H
#include <linux/types.h>
enum {
NCHAN = 4, /* # of HW channels */
MAX_MTU = 9600, /* max MAC MTU, excluding header + FCS */
EEPROMSIZE = 17408,/* Serial EEPROM physical size */
EEPROMVSIZE = 32768,/* Serial EEPROM virtual address space size */
EEPROMPFSIZE = 1024, /* EEPROM writable area size for PFn, n>0 */
RSS_NENTRIES = 2048, /* # of entries in RSS mapping table */
T6_RSS_NENTRIES = 4096, /* # of entries in RSS mapping table */
TCB_SIZE = 128, /* TCB size */
NMTUS = 16, /* size of MTU table */
NCCTRL_WIN = 32, /* # of congestion control windows */
NTX_SCHED = 8, /* # of HW Tx scheduling queues */
PM_NSTATS = 5, /* # of PM stats */
T6_PM_NSTATS = 7, /* # of PM stats in T6 */
MBOX_LEN = 64, /* mailbox size in bytes */
TRACE_LEN = 112, /* length of trace data and mask */
FILTER_OPT_LEN = 36, /* filter tuple width for optional components */
};
enum {
CIM_NUM_IBQ = 6, /* # of CIM IBQs */
CIM_NUM_OBQ = 6, /* # of CIM OBQs */
CIM_NUM_OBQ_T5 = 8, /* # of CIM OBQs for T5 adapter */
CIMLA_SIZE = 2048, /* # of 32-bit words in CIM LA */
CIM_PIFLA_SIZE = 64, /* # of 192-bit words in CIM PIF LA */
CIM_MALA_SIZE = 64, /* # of 160-bit words in CIM MA LA */
CIM_IBQ_SIZE = 128, /* # of 128-bit words in a CIM IBQ */
CIM_OBQ_SIZE = 128, /* # of 128-bit words in a CIM OBQ */
TPLA_SIZE = 128, /* # of 64-bit words in TP LA */
ULPRX_LA_SIZE = 512, /* # of 256-bit words in ULP_RX LA */
};
/* SGE context types */
enum ctxt_type {
CTXT_EGRESS,
CTXT_INGRESS,
CTXT_FLM,
CTXT_CNM,
};
enum {
SF_PAGE_SIZE = 256, /* serial flash page size */
SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */
};
enum { RSP_TYPE_FLBUF, RSP_TYPE_CPL, RSP_TYPE_INTR }; /* response entry types */
enum { MBOX_OWNER_NONE, MBOX_OWNER_FW, MBOX_OWNER_DRV }; /* mailbox owners */
enum {
SGE_MAX_WR_LEN = 512, /* max WR size in bytes */
SGE_CTXT_SIZE = 24, /* size of SGE context */
SGE_NTIMERS = 6, /* # of interrupt holdoff timer values */
SGE_NCOUNTERS = 4, /* # of interrupt packet counter values */
SGE_NDBQTIMERS = 8, /* # of Doorbell Queue Timer values */
SGE_MAX_IQ_SIZE = 65520,
SGE_TIMER_RSTRT_CNTR = 6, /* restart RX packet threshold counter */
SGE_TIMER_UPD_CIDX = 7, /* update cidx only */
SGE_EQ_IDXSIZE = 64, /* egress queue pidx/cidx unit size */
SGE_INTRDST_PCI = 0, /* interrupt destination is PCI-E */
SGE_INTRDST_IQ = 1, /* destination is an ingress queue */
SGE_UPDATEDEL_NONE = 0, /* ingress queue pidx update delivery */
SGE_UPDATEDEL_INTR = 1, /* interrupt */
SGE_UPDATEDEL_STPG = 2, /* status page */
SGE_UPDATEDEL_BOTH = 3, /* interrupt and status page */
SGE_HOSTFCMODE_NONE = 0, /* egress queue cidx updates */
SGE_HOSTFCMODE_IQ = 1, /* sent to ingress queue */
SGE_HOSTFCMODE_STPG = 2, /* sent to status page */
SGE_HOSTFCMODE_BOTH = 3, /* ingress queue and status page */
SGE_FETCHBURSTMIN_16B = 0,/* egress queue descriptor fetch minimum */
SGE_FETCHBURSTMIN_32B = 1,
SGE_FETCHBURSTMIN_64B = 2,
SGE_FETCHBURSTMIN_128B = 3,
SGE_FETCHBURSTMAX_64B = 0,/* egress queue descriptor fetch maximum */
SGE_FETCHBURSTMAX_128B = 1,
SGE_FETCHBURSTMAX_256B = 2,
SGE_FETCHBURSTMAX_512B = 3,
SGE_CIDXFLUSHTHRESH_1 = 0,/* egress queue cidx flush threshold */
SGE_CIDXFLUSHTHRESH_2 = 1,
SGE_CIDXFLUSHTHRESH_4 = 2,
SGE_CIDXFLUSHTHRESH_8 = 3,
SGE_CIDXFLUSHTHRESH_16 = 4,
SGE_CIDXFLUSHTHRESH_32 = 5,
SGE_CIDXFLUSHTHRESH_64 = 6,
SGE_CIDXFLUSHTHRESH_128 = 7,
SGE_INGPADBOUNDARY_SHIFT = 5,/* ingress queue pad boundary */
};
/* PCI-e memory window access */
enum pcie_memwin {
MEMWIN_NIC = 0,
MEMWIN_RSVD1 = 1,
MEMWIN_RSVD2 = 2,
MEMWIN_RDMA = 3,
MEMWIN_RSVD4 = 4,
MEMWIN_FOISCSI = 5,
MEMWIN_CSIOSTOR = 6,
MEMWIN_RSVD7 = 7,
};
struct sge_qstat { /* data written to SGE queue status entries */
__be32 qid;
__be16 cidx;
__be16 pidx;
};
/*
* Structure for last 128 bits of response descriptors
*/
struct rsp_ctrl {
__be32 hdrbuflen_pidx;
__be32 pldbuflen_qid;
union {
u8 type_gen;
__be64 last_flit;
};
};
#define RSPD_NEWBUF_S 31
#define RSPD_NEWBUF_V(x) ((x) << RSPD_NEWBUF_S)
#define RSPD_NEWBUF_F RSPD_NEWBUF_V(1U)
#define RSPD_LEN_S 0
#define RSPD_LEN_M 0x7fffffff
#define RSPD_LEN_G(x) (((x) >> RSPD_LEN_S) & RSPD_LEN_M)
#define RSPD_QID_S RSPD_LEN_S
#define RSPD_QID_M RSPD_LEN_M
#define RSPD_QID_G(x) RSPD_LEN_G(x)
#define RSPD_GEN_S 7
#define RSPD_TYPE_S 4
#define RSPD_TYPE_M 0x3
#define RSPD_TYPE_G(x) (((x) >> RSPD_TYPE_S) & RSPD_TYPE_M)
/* Rx queue interrupt deferral fields: counter enable and timer index */
#define QINTR_CNT_EN_S 0
#define QINTR_CNT_EN_V(x) ((x) << QINTR_CNT_EN_S)
#define QINTR_CNT_EN_F QINTR_CNT_EN_V(1U)
#define QINTR_TIMER_IDX_S 1
#define QINTR_TIMER_IDX_M 0x7
#define QINTR_TIMER_IDX_V(x) ((x) << QINTR_TIMER_IDX_S)
#define QINTR_TIMER_IDX_G(x) (((x) >> QINTR_TIMER_IDX_S) & QINTR_TIMER_IDX_M)
/*
* Flash layout.
*/
#define FLASH_START(start) ((start) * SF_SEC_SIZE)
#define FLASH_MAX_SIZE(nsecs) ((nsecs) * SF_SEC_SIZE)
enum {
/*
* Various Expansion-ROM boot images, etc.
*/
FLASH_EXP_ROM_START_SEC = 0,
FLASH_EXP_ROM_NSECS = 6,
FLASH_EXP_ROM_START = FLASH_START(FLASH_EXP_ROM_START_SEC),
FLASH_EXP_ROM_MAX_SIZE = FLASH_MAX_SIZE(FLASH_EXP_ROM_NSECS),
/*
* iSCSI Boot Firmware Table (iBFT) and other driver-related
* parameters ...
*/
FLASH_IBFT_START_SEC = 6,
FLASH_IBFT_NSECS = 1,
FLASH_IBFT_START = FLASH_START(FLASH_IBFT_START_SEC),
FLASH_IBFT_MAX_SIZE = FLASH_MAX_SIZE(FLASH_IBFT_NSECS),
/*
* Boot configuration data.
*/
FLASH_BOOTCFG_START_SEC = 7,
FLASH_BOOTCFG_NSECS = 1,
FLASH_BOOTCFG_START = FLASH_START(FLASH_BOOTCFG_START_SEC),
FLASH_BOOTCFG_MAX_SIZE = FLASH_MAX_SIZE(FLASH_BOOTCFG_NSECS),
/*
* Location of firmware image in FLASH.
*/
FLASH_FW_START_SEC = 8,
FLASH_FW_NSECS = 16,
FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC),
FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS),
/* Location of bootstrap firmware image in FLASH.
*/
FLASH_FWBOOTSTRAP_START_SEC = 27,
FLASH_FWBOOTSTRAP_NSECS = 1,
FLASH_FWBOOTSTRAP_START = FLASH_START(FLASH_FWBOOTSTRAP_START_SEC),
FLASH_FWBOOTSTRAP_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FWBOOTSTRAP_NSECS),
/*
* iSCSI persistent/crash information.
*/
FLASH_ISCSI_CRASH_START_SEC = 29,
FLASH_ISCSI_CRASH_NSECS = 1,
FLASH_ISCSI_CRASH_START = FLASH_START(FLASH_ISCSI_CRASH_START_SEC),
FLASH_ISCSI_CRASH_MAX_SIZE = FLASH_MAX_SIZE(FLASH_ISCSI_CRASH_NSECS),
/*
* FCoE persistent/crash information.
*/
FLASH_FCOE_CRASH_START_SEC = 30,
FLASH_FCOE_CRASH_NSECS = 1,
FLASH_FCOE_CRASH_START = FLASH_START(FLASH_FCOE_CRASH_START_SEC),
FLASH_FCOE_CRASH_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FCOE_CRASH_NSECS),
/*
* Location of Firmware Configuration File in FLASH. Since the FPGA
* "FLASH" is smaller we need to store the Configuration File in a
* different location -- which will overlap the end of the firmware
* image if firmware ever gets that large ...
*/
FLASH_CFG_START_SEC = 31,
FLASH_CFG_NSECS = 1,
FLASH_CFG_START = FLASH_START(FLASH_CFG_START_SEC),
FLASH_CFG_MAX_SIZE = FLASH_MAX_SIZE(FLASH_CFG_NSECS),
/* We don't support FLASH devices which can't support the full
* standard set of sections which we need for normal
* operations.
*/
FLASH_MIN_SIZE = FLASH_CFG_START + FLASH_CFG_MAX_SIZE,
FLASH_FPGA_CFG_START_SEC = 15,
FLASH_FPGA_CFG_START = FLASH_START(FLASH_FPGA_CFG_START_SEC),
/*
* Sectors 32-63 are reserved for FLASH failover.
*/
};
#undef FLASH_START
#undef FLASH_MAX_SIZE
#define SGE_TIMESTAMP_S 0
#define SGE_TIMESTAMP_M 0xfffffffffffffffULL
#define SGE_TIMESTAMP_V(x) ((__u64)(x) << SGE_TIMESTAMP_S)
#define SGE_TIMESTAMP_G(x) (((__u64)(x) >> SGE_TIMESTAMP_S) & SGE_TIMESTAMP_M)
#define I2C_DEV_ADDR_A0 0xa0
#define I2C_DEV_ADDR_A2 0xa2
#define I2C_PAGE_SIZE 0x100
#define SFP_DIAG_TYPE_ADDR 0x5c
#define SFP_DIAG_TYPE_LEN 0x1
#define SFP_DIAG_ADDRMODE BIT(2)
#define SFP_DIAG_IMPLEMENTED BIT(6)
#define SFF_8472_COMP_ADDR 0x5e
#define SFF_8472_COMP_LEN 0x1
#define SFF_REV_ADDR 0x1
#define SFF_REV_LEN 0x1
#endif /* __T4_HW_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,225 @@
/*
* This file is part of the Chelsio T4/T5 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __T4_PCI_ID_TBL_H__
#define __T4_PCI_ID_TBL_H__
/* The code can defined cpp macros for creating a PCI Device ID Table. This is
* useful because it allows the PCI ID Table to be maintained in a single place.
*
* The macros are:
*
* CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
* -- Used to start the definition of the PCI ID Table.
*
* CH_PCI_DEVICE_ID_FUNCTION
* -- The PCI Function Number to use in the PCI Device ID Table. "0"
* -- for drivers attaching to PF0-3, "4" for drivers attaching to PF4,
* -- "8" for drivers attaching to SR-IOV Virtual Functions, etc.
*
* CH_PCI_DEVICE_ID_FUNCTION2 [optional]
* -- If defined, create a PCI Device ID Table with both
* -- CH_PCI_DEVICE_ID_FUNCTION and CH_PCI_DEVICE_ID_FUNCTION2 populated.
*
* CH_PCI_ID_TABLE_ENTRY(DeviceID)
* -- Used for the individual PCI Device ID entries. Note that we will
* -- be adding a trailing comma (",") after all of the entries (and
* -- between the pairs of entries if CH_PCI_DEVICE_ID_FUNCTION2 is defined).
*
* CH_PCI_DEVICE_ID_TABLE_DEFINE_END
* -- Used to finish the definition of the PCI ID Table. Note that we
* -- will be adding a trailing semi-colon (";") here.
*/
#ifndef CH_PCI_DEVICE_ID_FUNCTION
#error CH_PCI_DEVICE_ID_FUNCTION not defined!
#endif
#ifndef CH_PCI_ID_TABLE_ENTRY
#error CH_PCI_ID_TABLE_ENTRY not defined!
#endif
#ifndef CH_PCI_DEVICE_ID_TABLE_DEFINE_END
#error CH_PCI_DEVICE_ID_TABLE_DEFINE_END not defined!
#endif
/* T4 and later ASICs use a PCI Device ID scheme of 0xVFPP where:
*
* V = "4" for T4; "5" for T5, etc.
* F = "0" for PF 0..3; "4".."7" for PF4..7; and "8" for VFs
* PP = adapter product designation
*
* We use this consistency in order to create the proper PCI Device IDs
* for the specified CH_PCI_DEVICE_ID_FUNCTION.
*/
#ifndef CH_PCI_DEVICE_ID_FUNCTION2
#define CH_PCI_ID_TABLE_FENTRY(devid) \
CH_PCI_ID_TABLE_ENTRY((devid) | \
((CH_PCI_DEVICE_ID_FUNCTION) << 8))
#else
#define CH_PCI_ID_TABLE_FENTRY(devid) \
CH_PCI_ID_TABLE_ENTRY((devid) | \
((CH_PCI_DEVICE_ID_FUNCTION) << 8)), \
CH_PCI_ID_TABLE_ENTRY((devid) | \
((CH_PCI_DEVICE_ID_FUNCTION2) << 8))
#endif
CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
/* T4 adapters:
*/
CH_PCI_ID_TABLE_FENTRY(0x4000), /* T440-dbg */
CH_PCI_ID_TABLE_FENTRY(0x4001), /* T420-cr */
CH_PCI_ID_TABLE_FENTRY(0x4002), /* T422-cr */
CH_PCI_ID_TABLE_FENTRY(0x4003), /* T440-cr */
CH_PCI_ID_TABLE_FENTRY(0x4004), /* T420-bch */
CH_PCI_ID_TABLE_FENTRY(0x4005), /* T440-bch */
CH_PCI_ID_TABLE_FENTRY(0x4006), /* T440-ch */
CH_PCI_ID_TABLE_FENTRY(0x4007), /* T420-so */
CH_PCI_ID_TABLE_FENTRY(0x4008), /* T420-cx */
CH_PCI_ID_TABLE_FENTRY(0x4009), /* T420-bt */
CH_PCI_ID_TABLE_FENTRY(0x400a), /* T404-bt */
CH_PCI_ID_TABLE_FENTRY(0x400b), /* B420-sr */
CH_PCI_ID_TABLE_FENTRY(0x400c), /* B404-bt */
CH_PCI_ID_TABLE_FENTRY(0x400d), /* T480-cr */
CH_PCI_ID_TABLE_FENTRY(0x400e), /* T440-LP-cr */
CH_PCI_ID_TABLE_FENTRY(0x4080), /* Custom T480-cr */
CH_PCI_ID_TABLE_FENTRY(0x4081), /* Custom T440-cr */
CH_PCI_ID_TABLE_FENTRY(0x4082), /* Custom T420-cr */
CH_PCI_ID_TABLE_FENTRY(0x4083), /* Custom T420-xaui */
CH_PCI_ID_TABLE_FENTRY(0x4084), /* Custom T440-cr */
CH_PCI_ID_TABLE_FENTRY(0x4085), /* Custom T420-cr */
CH_PCI_ID_TABLE_FENTRY(0x4086), /* Custom T440-bt */
CH_PCI_ID_TABLE_FENTRY(0x4087), /* Custom T440-cr */
CH_PCI_ID_TABLE_FENTRY(0x4088), /* Custom T440 2-xaui, 2-xfi */
/* T5 adapters:
*/
CH_PCI_ID_TABLE_FENTRY(0x5000), /* T580-dbg */
CH_PCI_ID_TABLE_FENTRY(0x5001), /* T520-cr */
CH_PCI_ID_TABLE_FENTRY(0x5002), /* T522-cr */
CH_PCI_ID_TABLE_FENTRY(0x5003), /* T540-cr */
CH_PCI_ID_TABLE_FENTRY(0x5004), /* T520-bch */
CH_PCI_ID_TABLE_FENTRY(0x5005), /* T540-bch */
CH_PCI_ID_TABLE_FENTRY(0x5006), /* T540-ch */
CH_PCI_ID_TABLE_FENTRY(0x5007), /* T520-so */
CH_PCI_ID_TABLE_FENTRY(0x5008), /* T520-cx */
CH_PCI_ID_TABLE_FENTRY(0x5009), /* T520-bt */
CH_PCI_ID_TABLE_FENTRY(0x500a), /* T504-bt */
CH_PCI_ID_TABLE_FENTRY(0x500b), /* B520-sr */
CH_PCI_ID_TABLE_FENTRY(0x500c), /* B504-bt */
CH_PCI_ID_TABLE_FENTRY(0x500d), /* T580-cr */
CH_PCI_ID_TABLE_FENTRY(0x500e), /* T540-LP-cr */
CH_PCI_ID_TABLE_FENTRY(0x5010), /* T580-LP-cr */
CH_PCI_ID_TABLE_FENTRY(0x5011), /* T520-LL-cr */
CH_PCI_ID_TABLE_FENTRY(0x5012), /* T560-cr */
CH_PCI_ID_TABLE_FENTRY(0x5013), /* T580-chr */
CH_PCI_ID_TABLE_FENTRY(0x5014), /* T580-so */
CH_PCI_ID_TABLE_FENTRY(0x5015), /* T502-bt */
CH_PCI_ID_TABLE_FENTRY(0x5016), /* T580-OCP-SO */
CH_PCI_ID_TABLE_FENTRY(0x5017), /* T520-OCP-SO */
CH_PCI_ID_TABLE_FENTRY(0x5018), /* T540-BT */
CH_PCI_ID_TABLE_FENTRY(0x5019), /* T540-LP-BT */
CH_PCI_ID_TABLE_FENTRY(0x501a), /* T540-SO-BT */
CH_PCI_ID_TABLE_FENTRY(0x501b), /* T540-SO-CR */
CH_PCI_ID_TABLE_FENTRY(0x5080), /* Custom T540-cr */
CH_PCI_ID_TABLE_FENTRY(0x5081), /* Custom T540-LL-cr */
CH_PCI_ID_TABLE_FENTRY(0x5082), /* Custom T504-cr */
CH_PCI_ID_TABLE_FENTRY(0x5083), /* Custom T540-LP-CR */
CH_PCI_ID_TABLE_FENTRY(0x5084), /* Custom T580-cr */
CH_PCI_ID_TABLE_FENTRY(0x5085), /* Custom 3x T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x5086), /* Custom 2x T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x5087), /* Custom T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x5088), /* Custom T570-CR */
CH_PCI_ID_TABLE_FENTRY(0x5089), /* Custom T520-CR */
CH_PCI_ID_TABLE_FENTRY(0x5090), /* Custom T540-CR */
CH_PCI_ID_TABLE_FENTRY(0x5091), /* Custom T522-CR */
CH_PCI_ID_TABLE_FENTRY(0x5092), /* Custom T520-CR */
CH_PCI_ID_TABLE_FENTRY(0x5093), /* Custom T580-LP-CR */
CH_PCI_ID_TABLE_FENTRY(0x5094), /* Custom T540-CR */
CH_PCI_ID_TABLE_FENTRY(0x5095), /* Custom T540-CR-SO */
CH_PCI_ID_TABLE_FENTRY(0x5096), /* Custom T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x5097), /* Custom T520-KR */
CH_PCI_ID_TABLE_FENTRY(0x5098), /* Custom 2x40G QSFP */
CH_PCI_ID_TABLE_FENTRY(0x5099), /* Custom 2x40G QSFP */
CH_PCI_ID_TABLE_FENTRY(0x509a), /* Custom T520-CR */
CH_PCI_ID_TABLE_FENTRY(0x509b), /* Custom T540-CR LOM */
CH_PCI_ID_TABLE_FENTRY(0x509c), /* Custom T520-CR*/
CH_PCI_ID_TABLE_FENTRY(0x509d), /* Custom T540-CR*/
CH_PCI_ID_TABLE_FENTRY(0x509e), /* Custom T520-CR */
CH_PCI_ID_TABLE_FENTRY(0x509f), /* Custom T540-CR */
CH_PCI_ID_TABLE_FENTRY(0x50a0), /* Custom T540-CR */
CH_PCI_ID_TABLE_FENTRY(0x50a1), /* Custom T540-CR */
CH_PCI_ID_TABLE_FENTRY(0x50a2), /* Custom T540-KR4 */
CH_PCI_ID_TABLE_FENTRY(0x50a3), /* Custom T580-KR4 */
CH_PCI_ID_TABLE_FENTRY(0x50a4), /* Custom 2x T540-CR */
CH_PCI_ID_TABLE_FENTRY(0x50a5), /* Custom T522-BT */
CH_PCI_ID_TABLE_FENTRY(0x50a6), /* Custom T522-BT-SO */
CH_PCI_ID_TABLE_FENTRY(0x50a7), /* Custom T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x50a8), /* Custom T580-KR */
CH_PCI_ID_TABLE_FENTRY(0x50a9), /* Custom T580-KR */
CH_PCI_ID_TABLE_FENTRY(0x50aa), /* Custom T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x50ab), /* Custom T520-CR */
CH_PCI_ID_TABLE_FENTRY(0x50ac), /* Custom T540-BT */
CH_PCI_ID_TABLE_FENTRY(0x50ad), /* Custom T520-CR */
CH_PCI_ID_TABLE_FENTRY(0x50ae), /* Custom T540-XL-SO */
CH_PCI_ID_TABLE_FENTRY(0x50af), /* Custom T580-KR-SO */
CH_PCI_ID_TABLE_FENTRY(0x50b0), /* Custom T520-CR-LOM */
/* T6 adapters:
*/
CH_PCI_ID_TABLE_FENTRY(0x6001),
CH_PCI_ID_TABLE_FENTRY(0x6002),
CH_PCI_ID_TABLE_FENTRY(0x6003),
CH_PCI_ID_TABLE_FENTRY(0x6004),
CH_PCI_ID_TABLE_FENTRY(0x6005),
CH_PCI_ID_TABLE_FENTRY(0x6006),
CH_PCI_ID_TABLE_FENTRY(0x6007),
CH_PCI_ID_TABLE_FENTRY(0x6008),
CH_PCI_ID_TABLE_FENTRY(0x6009),
CH_PCI_ID_TABLE_FENTRY(0x600d),
CH_PCI_ID_TABLE_FENTRY(0x6011),
CH_PCI_ID_TABLE_FENTRY(0x6014),
CH_PCI_ID_TABLE_FENTRY(0x6015),
CH_PCI_ID_TABLE_FENTRY(0x6080),
CH_PCI_ID_TABLE_FENTRY(0x6081),
CH_PCI_ID_TABLE_FENTRY(0x6082), /* Custom T6225-CR SFP28 */
CH_PCI_ID_TABLE_FENTRY(0x6083), /* Custom T62100-CR QSFP28 */
CH_PCI_ID_TABLE_FENTRY(0x6084), /* Custom T64100-CR QSFP28 */
CH_PCI_ID_TABLE_FENTRY(0x6085), /* Custom T6240-SO */
CH_PCI_ID_TABLE_FENTRY(0x6086), /* Custom T6225-SO-CR */
CH_PCI_ID_TABLE_FENTRY(0x6087), /* Custom T6225-CR */
CH_PCI_ID_TABLE_FENTRY(0x6088), /* Custom T62100-CR */
CH_PCI_ID_TABLE_FENTRY(0x6089), /* Custom T62100-KR */
CH_PCI_ID_TABLE_FENTRY(0x608a), /* Custom T62100-CR */
CH_PCI_ID_TABLE_FENTRY(0x608b), /* Custom T6225-CR */
CH_PCI_ID_TABLE_FENTRY(0x6092), /* Custom T62100-CR-LOM */
CH_PCI_DEVICE_ID_TABLE_DEFINE_END;
#endif /* __T4_PCI_ID_TBL_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,144 @@
/*
* This file is part of the Chelsio T4/T5/T6 Ethernet driver for Linux.
*
* Copyright (c) 2017 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __T4_TCB_H
#define __T4_TCB_H
#define TCB_L2T_IX_W 0
#define TCB_L2T_IX_S 12
#define TCB_L2T_IX_M 0xfffULL
#define TCB_L2T_IX_V(x) ((x) << TCB_L2T_IX_S)
#define TCB_T_FLAGS_W 1
#define TCB_T_FLAGS_S 0
#define TCB_T_FLAGS_M 0xffffffffffffffffULL
#define TCB_T_FLAGS_V(x) ((__u64)(x) << TCB_T_FLAGS_S)
#define TCB_FIELD_COOKIE_TFLAG 1
#define TCB_SMAC_SEL_W 0
#define TCB_SMAC_SEL_S 24
#define TCB_SMAC_SEL_M 0xffULL
#define TCB_SMAC_SEL_V(x) ((x) << TCB_SMAC_SEL_S)
#define TCB_T_FLAGS_W 1
#define TCB_T_FLAGS_S 0
#define TCB_T_FLAGS_M 0xffffffffffffffffULL
#define TCB_T_FLAGS_V(x) ((__u64)(x) << TCB_T_FLAGS_S)
#define TF_DROP_S 22
#define TF_DIRECT_STEER_S 23
#define TF_LPBK_S 59
#define TF_CCTRL_ECE_S 60
#define TF_CCTRL_CWR_S 61
#define TF_CCTRL_RFR_S 62
#define TCB_RSS_INFO_W 3
#define TCB_RSS_INFO_S 0
#define TCB_RSS_INFO_M 0x3ffULL
#define TCB_RSS_INFO_V(x) ((x) << TCB_RSS_INFO_S)
#define TCB_T_STATE_W 3
#define TCB_T_STATE_S 16
#define TCB_T_STATE_M 0xfULL
#define TCB_T_STATE_V(x) ((x) << TCB_T_STATE_S)
#define TCB_TIMESTAMP_W 5
#define TCB_TIMESTAMP_S 0
#define TCB_TIMESTAMP_M 0xffffffffULL
#define TCB_TIMESTAMP_V(x) ((x) << TCB_TIMESTAMP_S)
#define TCB_RTT_TS_RECENT_AGE_W 6
#define TCB_RTT_TS_RECENT_AGE_S 0
#define TCB_RTT_TS_RECENT_AGE_M 0xffffffffULL
#define TCB_RTT_TS_RECENT_AGE_V(x) ((x) << TCB_RTT_TS_RECENT_AGE_S)
#define TCB_T_RTSEQ_RECENT_W 7
#define TCB_T_RTSEQ_RECENT_S 0
#define TCB_T_RTSEQ_RECENT_M 0xffffffffULL
#define TCB_T_RTSEQ_RECENT_V(x) ((x) << TCB_T_RTSEQ_RECENT_S)
#define TCB_TX_MAX_W 9
#define TCB_TX_MAX_S 0
#define TCB_TX_MAX_M 0xffffffffULL
#define TCB_TX_MAX_V(x) ((x) << TCB_TX_MAX_S)
#define TCB_SND_UNA_RAW_W 10
#define TCB_SND_UNA_RAW_S 0
#define TCB_SND_UNA_RAW_M 0xfffffffULL
#define TCB_SND_UNA_RAW_V(x) ((x) << TCB_SND_UNA_RAW_S)
#define TCB_SND_NXT_RAW_W 10
#define TCB_SND_NXT_RAW_S 28
#define TCB_SND_NXT_RAW_M 0xfffffffULL
#define TCB_SND_NXT_RAW_V(x) ((x) << TCB_SND_NXT_RAW_S)
#define TCB_SND_MAX_RAW_W 11
#define TCB_SND_MAX_RAW_S 24
#define TCB_SND_MAX_RAW_M 0xfffffffULL
#define TCB_SND_MAX_RAW_V(x) ((x) << TCB_SND_MAX_RAW_S)
#define TCB_RCV_NXT_W 16
#define TCB_RCV_NXT_S 10
#define TCB_RCV_NXT_M 0xffffffffULL
#define TCB_RCV_NXT_V(x) ((x) << TCB_RCV_NXT_S)
#define TCB_RCV_WND_W 17
#define TCB_RCV_WND_S 10
#define TCB_RCV_WND_M 0xffffffULL
#define TCB_RCV_WND_V(x) ((x) << TCB_RCV_WND_S)
#define TCB_RX_FRAG2_PTR_RAW_W 27
#define TCB_RX_FRAG3_LEN_RAW_W 29
#define TCB_RX_FRAG3_START_IDX_OFFSET_RAW_W 30
#define TCB_PDU_HDR_LEN_W 31
#define TCB_RQ_START_W 30
#define TCB_RQ_START_S 0
#define TCB_RQ_START_M 0x3ffffffULL
#define TCB_RQ_START_V(x) ((x) << TCB_RQ_START_S)
#define TF_RX_PDU_OUT_S 49
#define TF_RX_PDU_OUT_V(x) ((__u64)(x) << TF_RX_PDU_OUT_S)
#define TF_CORE_BYPASS_S 63
#define TF_CORE_BYPASS_V(x) ((__u64)(x) << TF_CORE_BYPASS_S)
#define TF_CORE_BYPASS_F TF_CORE_BYPASS_V(1)
#define TF_NON_OFFLOAD_S 1
#define TF_NON_OFFLOAD_V(x) ((x) << TF_NON_OFFLOAD_S)
#define TF_NON_OFFLOAD_F TF_NON_OFFLOAD_V(1)
#endif /* __T4_TCB_H */
@@ -0,0 +1,162 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __T4_VALUES_H__
#define __T4_VALUES_H__
/* This file contains definitions for various T4 register value hardware
* constants. The types of values encoded here are predominantly those for
* register fields which control "modal" behavior. For the most part, we do
* not include definitions for register fields which are simple numeric
* metrics, etc.
*/
/* SGE register field values.
*/
/* CONTROL1 register */
#define RXPKTCPLMODE_SPLIT_X 1
#define INGPCIEBOUNDARY_SHIFT_X 5
#define INGPCIEBOUNDARY_32B_X 0
#define INGPADBOUNDARY_SHIFT_X 5
#define T6_INGPADBOUNDARY_SHIFT_X 3
#define T6_INGPADBOUNDARY_8B_X 0
#define T6_INGPADBOUNDARY_32B_X 2
#define INGPADBOUNDARY_32B_X 0
/* CONTROL2 register */
#define INGPACKBOUNDARY_SHIFT_X 5
#define INGPACKBOUNDARY_16B_X 0
#define INGPACKBOUNDARY_64B_X 1
/* GTS register */
#define SGE_TIMERREGS 6
#define TIMERREG_COUNTER0_X 0
#define FETCHBURSTMIN_64B_X 2
#define FETCHBURSTMIN_128B_X 3
/* T6 and later use a single-bit encoding for FetchBurstMin */
#define FETCHBURSTMIN_64B_T6_X 0
#define FETCHBURSTMIN_128B_T6_X 1
#define FETCHBURSTMAX_256B_X 2
#define FETCHBURSTMAX_512B_X 3
#define HOSTFCMODE_INGRESS_QUEUE_X 1
#define HOSTFCMODE_STATUS_PAGE_X 2
#define CIDXFLUSHTHRESH_32_X 5
#define CIDXFLUSHTHRESH_128_X 7
#define UPDATEDELIVERY_INTERRUPT_X 1
#define RSPD_TYPE_FLBUF_X 0
#define RSPD_TYPE_CPL_X 1
#define RSPD_TYPE_INTR_X 2
/* Congestion Manager Definitions.
*/
#define CONMCTXT_CNGTPMODE_S 19
#define CONMCTXT_CNGTPMODE_V(x) ((x) << CONMCTXT_CNGTPMODE_S)
#define CONMCTXT_CNGCHMAP_S 0
#define CONMCTXT_CNGCHMAP_V(x) ((x) << CONMCTXT_CNGCHMAP_S)
#define CONMCTXT_CNGTPMODE_CHANNEL_X 2
#define CONMCTXT_CNGTPMODE_QUEUE_X 1
/* T5 and later support a new BAR2-based doorbell mechanism for Egress Queues.
* The User Doorbells are each 128 bytes in length with a Simple Doorbell at
* offsets 8x and a Write Combining single 64-byte Egress Queue Unit
* (IDXSIZE_UNIT_X) Gather Buffer interface at offset 64. For Ingress Queues,
* we have a Going To Sleep register at offsets 8x+4.
*
* As noted above, we have many instances of the Simple Doorbell and Going To
* Sleep registers at offsets 8x and 8x+4, respectively. We want to use a
* non-64-byte aligned offset for the Simple Doorbell in order to attempt to
* avoid buffering of the writes to the Simple Doorbell and we want to use a
* non-contiguous offset for the Going To Sleep writes in order to avoid
* possible combining between them.
*/
#define SGE_UDB_SIZE 128
#define SGE_UDB_KDOORBELL 8
#define SGE_UDB_GTS 20
#define SGE_UDB_WCDOORBELL 64
/* CIM register field values.
*/
#define X_MBOWNER_FW 1
#define X_MBOWNER_PL 2
/* PCI-E definitions */
#define WINDOW_SHIFT_X 10
#define PCIEOFST_SHIFT_X 10
/* TP_VLAN_PRI_MAP controls which subset of fields will be present in the
* Compressed Filter Tuple for LE filters. Each bit set in TP_VLAN_PRI_MAP
* selects for a particular field being present. These fields, when present
* in the Compressed Filter Tuple, have the following widths in bits.
*/
#define FT_FCOE_W 1
#define FT_PORT_W 3
#define FT_VNIC_ID_W 17
#define FT_VLAN_W 17
#define FT_TOS_W 8
#define FT_PROTOCOL_W 8
#define FT_ETHERTYPE_W 16
#define FT_MACMATCH_W 9
#define FT_MPSHITTYPE_W 3
#define FT_FRAGMENTATION_W 1
/* Some of the Compressed Filter Tuple fields have internal structure. These
* bit shifts/masks describe those structures. All shifts are relative to the
* base position of the fields within the Compressed Filter Tuple
*/
#define FT_VLAN_VLD_S 16
#define FT_VLAN_VLD_V(x) ((x) << FT_VLAN_VLD_S)
#define FT_VLAN_VLD_F FT_VLAN_VLD_V(1U)
#define FT_VNID_ID_VF_S 0
#define FT_VNID_ID_VF_V(x) ((x) << FT_VNID_ID_VF_S)
#define FT_VNID_ID_PF_S 7
#define FT_VNID_ID_PF_V(x) ((x) << FT_VNID_ID_PF_S)
#define FT_VNID_ID_VLD_S 16
#define FT_VNID_ID_VLD_V(x) ((x) << FT_VNID_ID_VLD_S)
#endif /* __T4_VALUES_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __T4FW_VERSION_H__
#define __T4FW_VERSION_H__
#define T4FW_VERSION_MAJOR 0x01
#define T4FW_VERSION_MINOR 0x17
#define T4FW_VERSION_MICRO 0x03
#define T4FW_VERSION_BUILD 0x00
#define T4FW_MIN_VERSION_MAJOR 0x01
#define T4FW_MIN_VERSION_MINOR 0x04
#define T4FW_MIN_VERSION_MICRO 0x00
#define T5FW_VERSION_MAJOR 0x01
#define T5FW_VERSION_MINOR 0x17
#define T5FW_VERSION_MICRO 0x03
#define T5FW_VERSION_BUILD 0x00
#define T5FW_MIN_VERSION_MAJOR 0x00
#define T5FW_MIN_VERSION_MINOR 0x00
#define T5FW_MIN_VERSION_MICRO 0x00
#define T6FW_VERSION_MAJOR 0x01
#define T6FW_VERSION_MINOR 0x17
#define T6FW_VERSION_MICRO 0x03
#define T6FW_VERSION_BUILD 0x00
#define T6FW_MIN_VERSION_MAJOR 0x00
#define T6FW_MIN_VERSION_MINOR 0x00
#define T6FW_MIN_VERSION_MICRO 0x00
#endif