Merge branch 'thunderx-DMAC-filtering'

Vadim Lomovtsev says:

====================
net: thunderx: implement DMAC filtering support

By default CN88XX BGX accepts all incoming multicast and broadcast
packets and filtering is disabled. The nic driver doesn't provide
an ability to change such behaviour.

This series is to implement DMAC filtering management for CN88XX
nic driver allowing user to enable/disable filtering and configure
specific MAC addresses to filter traffic.

Changes from v1:
build issues:
 - update code in order to address compiler warnings;
checkpatch.pl reported issues:
 - update code in order to fit 80 symbols length;
 - update commit descriptions in order to fit 80 symbols length;
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-03-31 22:18:27 -04:00
commit 56c03cbf8c
5 changed files with 374 additions and 30 deletions

View File

@ -265,6 +265,22 @@ struct nicvf_drv_stats {
struct cavium_ptp;
struct xcast_addr {
struct list_head list;
u64 addr;
};
struct xcast_addr_list {
struct list_head list;
int count;
};
struct nicvf_work {
struct delayed_work work;
u8 mode;
struct xcast_addr_list *mc;
};
struct nicvf {
struct nicvf *pnicvf;
struct net_device *netdev;
@ -313,6 +329,7 @@ struct nicvf {
struct nicvf_pfc pfc;
struct tasklet_struct qs_err_task;
struct work_struct reset_task;
struct nicvf_work rx_mode_work;
/* PTP timestamp */
struct cavium_ptp *ptp_clock;
@ -403,6 +420,9 @@ struct nicvf {
#define NIC_MBOX_MSG_PTP_CFG 0x19 /* HW packet timestamp */
#define NIC_MBOX_MSG_CFG_DONE 0xF0 /* VF configuration done */
#define NIC_MBOX_MSG_SHUTDOWN 0xF1 /* VF is being shutdown */
#define NIC_MBOX_MSG_RESET_XCAST 0xF2 /* Reset DCAM filtering mode */
#define NIC_MBOX_MSG_ADD_MCAST 0xF3 /* Add MAC to DCAM filters */
#define NIC_MBOX_MSG_SET_XCAST 0xF4 /* Set MCAST/BCAST RX mode */
struct nic_cfg_msg {
u8 msg;
@ -556,6 +576,14 @@ struct set_ptp {
bool enable;
};
struct xcast {
u8 msg;
union {
u8 mode;
u64 mac;
} data;
};
/* 128 bit shared memory between PF and each VF */
union nic_mbx {
struct { u8 msg; } msg;
@ -576,6 +604,7 @@ union nic_mbx {
struct reset_stat_cfg reset_stat;
struct pfc pfc;
struct set_ptp ptp;
struct xcast xcast;
};
#define NIC_NODE_ID_MASK 0x03

View File

@ -21,6 +21,8 @@
#define DRV_NAME "nicpf"
#define DRV_VERSION "1.0"
#define NIC_VF_PER_MBX_REG 64
struct hw_info {
u8 bgx_cnt;
u8 chans_per_lmac;
@ -1072,6 +1074,40 @@ static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
case NIC_MBOX_MSG_PTP_CFG:
nic_config_timestamp(nic, vf, &mbx.ptp);
break;
case NIC_MBOX_MSG_RESET_XCAST:
if (vf >= nic->num_vf_en) {
ret = -1; /* NACK */
break;
}
bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
bgx_reset_xcast_mode(nic->node, bgx, lmac,
vf < NIC_VF_PER_MBX_REG ? vf :
vf - NIC_VF_PER_MBX_REG);
break;
case NIC_MBOX_MSG_ADD_MCAST:
if (vf >= nic->num_vf_en) {
ret = -1; /* NACK */
break;
}
bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
bgx_set_dmac_cam_filter(nic->node, bgx, lmac,
mbx.xcast.data.mac,
vf < NIC_VF_PER_MBX_REG ? vf :
vf - NIC_VF_PER_MBX_REG);
break;
case NIC_MBOX_MSG_SET_XCAST:
if (vf >= nic->num_vf_en) {
ret = -1; /* NACK */
break;
}
bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
bgx_set_xcast_mode(nic->node, bgx, lmac, mbx.xcast.data.mode);
break;
default:
dev_err(&nic->pdev->dev,
"Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg);
@ -1094,7 +1130,7 @@ static irqreturn_t nic_mbx_intr_handler(int irq, void *nic_irq)
struct nicpf *nic = (struct nicpf *)nic_irq;
int mbx;
u64 intr;
u8 vf, vf_per_mbx_reg = 64;
u8 vf;
if (irq == pci_irq_vector(nic->pdev, NIC_PF_INTR_ID_MBOX0))
mbx = 0;
@ -1103,12 +1139,13 @@ static irqreturn_t nic_mbx_intr_handler(int irq, void *nic_irq)
intr = nic_reg_read(nic, NIC_PF_MAILBOX_INT + (mbx << 3));
dev_dbg(&nic->pdev->dev, "PF interrupt Mbox%d 0x%llx\n", mbx, intr);
for (vf = 0; vf < vf_per_mbx_reg; vf++) {
for (vf = 0; vf < NIC_VF_PER_MBX_REG; vf++) {
if (intr & (1ULL << vf)) {
dev_dbg(&nic->pdev->dev, "Intr from VF %d\n",
vf + (mbx * vf_per_mbx_reg));
vf + (mbx * NIC_VF_PER_MBX_REG));
nic_handle_mbx_intr(nic, vf + (mbx * vf_per_mbx_reg));
nic_handle_mbx_intr(nic, vf +
(mbx * NIC_VF_PER_MBX_REG));
nic_clear_mbx_intr(nic, vf, mbx);
}
}

View File

@ -21,6 +21,7 @@
#include <linux/bpf_trace.h>
#include <linux/filter.h>
#include <linux/net_tstamp.h>
#include <linux/workqueue.h>
#include "nic_reg.h"
#include "nic.h"
@ -67,6 +68,9 @@ module_param(cpi_alg, int, 0444);
MODULE_PARM_DESC(cpi_alg,
"PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)");
/* workqueue for handling kernel ndo_set_rx_mode() calls */
static struct workqueue_struct *nicvf_rx_mode_wq;
static inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx)
{
if (nic->sqs_mode)
@ -1919,6 +1923,100 @@ static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
}
}
static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
{
struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
work.work);
struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
union nic_mbx mbx = {};
struct xcast_addr *xaddr, *next;
if (!vf_work)
return;
/* From the inside of VM code flow we have only 128 bits memory
* available to send message to host's PF, so send all mc addrs
* one by one, starting from flush command in case if kernel
* requests to configure specific MAC filtering
*/
/* flush DMAC filters and reset RX mode */
mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST;
nicvf_send_msg_to_pf(nic, &mbx);
if (vf_work->mode & BGX_XCAST_MCAST_FILTER) {
/* once enabling filtering, we need to signal to PF to add
* its' own LMAC to the filter to accept packets for it.
*/
mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
mbx.xcast.data.mac = 0;
nicvf_send_msg_to_pf(nic, &mbx);
}
/* check if we have any specific MACs to be added to PF DMAC filter */
if (vf_work->mc) {
/* now go through kernel list of MACs and add them one by one */
list_for_each_entry_safe(xaddr, next,
&vf_work->mc->list, list) {
mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
mbx.xcast.data.mac = xaddr->addr;
nicvf_send_msg_to_pf(nic, &mbx);
/* after receiving ACK from PF release memory */
list_del(&xaddr->list);
kfree(xaddr);
vf_work->mc->count--;
}
kfree(vf_work->mc);
}
/* and finally set rx mode for PF accordingly */
mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST;
mbx.xcast.data.mode = vf_work->mode;
nicvf_send_msg_to_pf(nic, &mbx);
}
static void nicvf_set_rx_mode(struct net_device *netdev)
{
struct nicvf *nic = netdev_priv(netdev);
struct netdev_hw_addr *ha;
struct xcast_addr_list *mc_list = NULL;
u8 mode = 0;
if (netdev->flags & IFF_PROMISC) {
mode = BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT;
} else {
if (netdev->flags & IFF_BROADCAST)
mode |= BGX_XCAST_BCAST_ACCEPT;
if (netdev->flags & IFF_ALLMULTI) {
mode |= BGX_XCAST_MCAST_ACCEPT;
} else if (netdev->flags & IFF_MULTICAST) {
mode |= BGX_XCAST_MCAST_FILTER;
/* here we need to copy mc addrs */
if (netdev_mc_count(netdev)) {
struct xcast_addr *xaddr;
mc_list = kmalloc(sizeof(*mc_list), GFP_ATOMIC);
INIT_LIST_HEAD(&mc_list->list);
netdev_hw_addr_list_for_each(ha, &netdev->mc) {
xaddr = kmalloc(sizeof(*xaddr),
GFP_ATOMIC);
xaddr->addr =
ether_addr_to_u64(ha->addr);
list_add_tail(&xaddr->list,
&mc_list->list);
mc_list->count++;
}
}
}
}
nic->rx_mode_work.mc = mc_list;
nic->rx_mode_work.mode = mode;
queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 2 * HZ);
}
static const struct net_device_ops nicvf_netdev_ops = {
.ndo_open = nicvf_open,
.ndo_stop = nicvf_stop,
@ -1931,6 +2029,7 @@ static const struct net_device_ops nicvf_netdev_ops = {
.ndo_set_features = nicvf_set_features,
.ndo_bpf = nicvf_xdp,
.ndo_do_ioctl = nicvf_ioctl,
.ndo_set_rx_mode = nicvf_set_rx_mode,
};
static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@ -2071,6 +2170,8 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
INIT_WORK(&nic->reset_task, nicvf_reset_task);
INIT_DELAYED_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
err = register_netdev(netdev);
if (err) {
dev_err(dev, "Failed to register netdevice\n");
@ -2109,6 +2210,8 @@ static void nicvf_remove(struct pci_dev *pdev)
nic = netdev_priv(netdev);
pnetdev = nic->pnicvf->netdev;
cancel_delayed_work_sync(&nic->rx_mode_work.work);
/* Check if this Qset is assigned to different VF.
* If yes, clean primary and all secondary Qsets.
*/
@ -2140,12 +2243,17 @@ static struct pci_driver nicvf_driver = {
static int __init nicvf_init_module(void)
{
pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION);
nicvf_rx_mode_wq = alloc_ordered_workqueue("nicvf_generic",
WQ_MEM_RECLAIM);
return pci_register_driver(&nicvf_driver);
}
static void __exit nicvf_cleanup_module(void)
{
if (nicvf_rx_mode_wq) {
destroy_workqueue(nicvf_rx_mode_wq);
nicvf_rx_mode_wq = NULL;
}
pci_unregister_driver(&nicvf_driver);
}

View File

@ -24,9 +24,31 @@
#define DRV_NAME "thunder_bgx"
#define DRV_VERSION "1.0"
/* RX_DMAC_CTL configuration */
enum MCAST_MODE {
MCAST_MODE_REJECT = 0x0,
MCAST_MODE_ACCEPT = 0x1,
MCAST_MODE_CAM_FILTER = 0x2,
RSVD = 0x3
};
#define BCAST_ACCEPT BIT(0)
#define CAM_ACCEPT BIT(3)
#define MCAST_MODE_MASK 0x3
#define BGX_MCAST_MODE(x) (x << 1)
struct dmac_map {
u64 vf_map;
u64 dmac;
};
struct lmac {
struct bgx *bgx;
int dmac;
/* actual number of DMACs configured */
u8 dmacs_cfg;
/* overal number of possible DMACs could be configured per LMAC */
u8 dmacs_count;
struct dmac_map *dmacs; /* DMAC:VFs tracking filter array */
u8 mac[ETH_ALEN];
u8 lmac_type;
u8 lane_to_sds;
@ -223,6 +245,163 @@ void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac)
}
EXPORT_SYMBOL(bgx_set_lmac_mac);
static void bgx_flush_dmac_cam_filter(struct bgx *bgx, int lmacid)
{
struct lmac *lmac = NULL;
u8 idx = 0;
lmac = &bgx->lmac[lmacid];
/* reset CAM filters */
for (idx = 0; idx < lmac->dmacs_count; idx++)
bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
((lmacid * lmac->dmacs_count) + idx) *
sizeof(u64), 0);
}
static void bgx_lmac_remove_filters(struct lmac *lmac, u8 vf_id)
{
int i = 0;
if (!lmac)
return;
/* We've got reset filters request from some of attached VF, while the
* others might want to keep their configuration. So in this case lets
* iterate over all of configured filters and decrease number of
* referencies. if some addresses get zero refs remove them from list
*/
for (i = lmac->dmacs_cfg - 1; i >= 0; i--) {
lmac->dmacs[i].vf_map &= ~BIT_ULL(vf_id);
if (!lmac->dmacs[i].vf_map) {
lmac->dmacs_cfg--;
lmac->dmacs[i].dmac = 0;
lmac->dmacs[i].vf_map = 0;
}
}
}
static int bgx_lmac_save_filter(struct lmac *lmac, u64 dmac, u8 vf_id)
{
u8 i = 0;
if (!lmac)
return -1;
/* At the same time we could have several VFs 'attached' to some
* particular LMAC, and each VF is represented as network interface
* for kernel. So from user perspective it should be possible to
* manipulate with its' (VF) receive modes. However from PF
* driver perspective we need to keep track of filter configurations
* for different VFs to prevent filter values dupes
*/
for (i = 0; i < lmac->dmacs_cfg; i++) {
if (lmac->dmacs[i].dmac == dmac) {
lmac->dmacs[i].vf_map |= BIT_ULL(vf_id);
return -1;
}
}
if (!(lmac->dmacs_cfg < lmac->dmacs_count))
return -1;
/* keep it for further tracking */
lmac->dmacs[lmac->dmacs_cfg].dmac = dmac;
lmac->dmacs[lmac->dmacs_cfg].vf_map = BIT_ULL(vf_id);
lmac->dmacs_cfg++;
return 0;
}
static int bgx_set_dmac_cam_filter_mac(struct bgx *bgx, int lmacid,
u64 cam_dmac, u8 idx)
{
struct lmac *lmac = NULL;
u64 cfg = 0;
/* skip zero addresses as meaningless */
if (!cam_dmac || !bgx)
return -1;
lmac = &bgx->lmac[lmacid];
/* configure DCAM filtering for designated LMAC */
cfg = RX_DMACX_CAM_LMACID(lmacid & LMAC_ID_MASK) |
RX_DMACX_CAM_EN | cam_dmac;
bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
((lmacid * lmac->dmacs_count) + idx) * sizeof(u64), cfg);
return 0;
}
void bgx_set_dmac_cam_filter(int node, int bgx_idx, int lmacid,
u64 cam_dmac, u8 vf_id)
{
struct bgx *bgx = get_bgx(node, bgx_idx);
struct lmac *lmac = NULL;
if (!bgx)
return;
lmac = &bgx->lmac[lmacid];
if (!cam_dmac)
cam_dmac = ether_addr_to_u64(lmac->mac);
/* since we might have several VFs attached to particular LMAC
* and kernel could call mcast config for each of them with the
* same MAC, check if requested MAC is already in filtering list and
* updare/prepare list of MACs to be applied later to HW filters
*/
bgx_lmac_save_filter(lmac, cam_dmac, vf_id);
}
EXPORT_SYMBOL(bgx_set_dmac_cam_filter);
void bgx_set_xcast_mode(int node, int bgx_idx, int lmacid, u8 mode)
{
struct bgx *bgx = get_bgx(node, bgx_idx);
struct lmac *lmac = NULL;
u64 cfg = 0;
u8 i = 0;
if (!bgx)
return;
lmac = &bgx->lmac[lmacid];
cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL);
if (mode & BGX_XCAST_BCAST_ACCEPT)
cfg |= BCAST_ACCEPT;
else
cfg &= ~BCAST_ACCEPT;
/* disable all MCASTs and DMAC filtering */
cfg &= ~(CAM_ACCEPT | BGX_MCAST_MODE(MCAST_MODE_MASK));
/* check requested bits and set filtergin mode appropriately */
if (mode & (BGX_XCAST_MCAST_ACCEPT)) {
cfg |= (BGX_MCAST_MODE(MCAST_MODE_ACCEPT));
} else if (mode & BGX_XCAST_MCAST_FILTER) {
cfg |= (BGX_MCAST_MODE(MCAST_MODE_CAM_FILTER) | CAM_ACCEPT);
for (i = 0; i < lmac->dmacs_cfg; i++)
bgx_set_dmac_cam_filter_mac(bgx, lmacid,
lmac->dmacs[i].dmac, i);
}
bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, cfg);
}
EXPORT_SYMBOL(bgx_set_xcast_mode);
void bgx_reset_xcast_mode(int node, int bgx_idx, int lmacid, u8 vf_id)
{
struct bgx *bgx = get_bgx(node, bgx_idx);
if (!bgx)
return;
bgx_lmac_remove_filters(&bgx->lmac[lmacid], vf_id);
bgx_flush_dmac_cam_filter(bgx, lmacid);
bgx_set_xcast_mode(node, bgx_idx, lmacid,
(BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT));
}
EXPORT_SYMBOL(bgx_reset_xcast_mode);
void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
{
struct bgx *bgx = get_bgx(node, bgx_idx);
@ -468,18 +647,6 @@ u64 bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx)
}
EXPORT_SYMBOL(bgx_get_tx_stats);
static void bgx_flush_dmac_addrs(struct bgx *bgx, int lmac)
{
u64 offset;
while (bgx->lmac[lmac].dmac > 0) {
offset = ((bgx->lmac[lmac].dmac - 1) * sizeof(u64)) +
(lmac * MAX_DMAC_PER_LMAC * sizeof(u64));
bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + offset, 0);
bgx->lmac[lmac].dmac--;
}
}
/* Configure BGX LMAC in internal loopback mode */
void bgx_lmac_internal_loopback(int node, int bgx_idx,
int lmac_idx, bool enable)
@ -912,6 +1079,11 @@ static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4);
}
/* actual number of filters available to exact LMAC */
lmac->dmacs_count = (RX_DMAC_COUNT / bgx->lmac_count);
lmac->dmacs = kcalloc(lmac->dmacs_count, sizeof(*lmac->dmacs),
GFP_KERNEL);
/* Enable lmac */
bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
@ -998,7 +1170,8 @@ static void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
cfg &= ~CMR_EN;
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
bgx_flush_dmac_addrs(bgx, lmacid);
bgx_flush_dmac_cam_filter(bgx, lmacid);
kfree(lmac->dmacs);
if ((lmac->lmac_type != BGX_MODE_XFI) &&
(lmac->lmac_type != BGX_MODE_XLAUI) &&

View File

@ -30,6 +30,7 @@
#define DEFAULT_PAUSE_TIME 0xFFFF
#define BGX_ID_MASK 0x3
#define LMAC_ID_MASK 0x3
#define MAX_DMAC_PER_LMAC_TNS_BYPASS_MODE 2
@ -57,7 +58,7 @@
#define BGX_CMRX_RX_FIFO_LEN 0x108
#define BGX_CMR_RX_DMACX_CAM 0x200
#define RX_DMACX_CAM_EN BIT_ULL(48)
#define RX_DMACX_CAM_LMACID(x) (x << 49)
#define RX_DMACX_CAM_LMACID(x) (((u64)x) << 49)
#define RX_DMAC_COUNT 32
#define BGX_CMR_RX_STREERING 0x300
#define RX_TRAFFIC_STEER_RULE_COUNT 8
@ -205,17 +206,13 @@
#define LMAC_INTR_LINK_UP BIT(0)
#define LMAC_INTR_LINK_DOWN BIT(1)
/* RX_DMAC_CTL configuration*/
enum MCAST_MODE {
MCAST_MODE_REJECT,
MCAST_MODE_ACCEPT,
MCAST_MODE_CAM_FILTER,
RSVD
};
#define BCAST_ACCEPT 1
#define CAM_ACCEPT 1
#define BGX_XCAST_BCAST_ACCEPT BIT(0)
#define BGX_XCAST_MCAST_ACCEPT BIT(1)
#define BGX_XCAST_MCAST_FILTER BIT(2)
void bgx_set_dmac_cam_filter(int node, int bgx_idx, int lmacid, u64 mac, u8 vf);
void bgx_reset_xcast_mode(int node, int bgx_idx, int lmacid, u8 vf);
void bgx_set_xcast_mode(int node, int bgx_idx, int lmacid, u8 mode);
void octeon_mdiobus_force_mod_depencency(void);
void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable);
void bgx_add_dmac_addr(u64 dmac, int node, int bgx_idx, int lmac);