bc2a13136a
BCMSAI 4.3.0.10, 6.5.21 SDK release with enhancements and fixes for vxlan, TD3 MMU, TD4-X9 EA support, etc.
445 lines
11 KiB
C
445 lines
11 KiB
C
/*! \file ngknetcb_main.c
|
|
*
|
|
* NGKNET Callback module entry.
|
|
*/
|
|
/*
|
|
* $Copyright: (c) 2019 Broadcom.
|
|
* Broadcom Proprietary and Confidential. All rights reserved.$
|
|
*/
|
|
|
|
#include <lkm/lkm.h>
|
|
#include <ngknet_callback.h>
|
|
#include "psample-cb.h"
|
|
|
|
/*! \cond */
|
|
MODULE_AUTHOR("Broadcom Corporation");
|
|
MODULE_DESCRIPTION("NGKNET Callback Module");
|
|
MODULE_LICENSE("GPL");
|
|
/*! \endcond */
|
|
|
|
/*! \cond */
|
|
int debug = 0;
|
|
MODULE_PARAM(debug, int, 0);
|
|
MODULE_PARM_DESC(debug,
|
|
"Debug level (default 0)");
|
|
/*! \endcond */
|
|
|
|
/*! Module information */
|
|
#define NGKNETCB_MODULE_NAME "linux_ngknetcb"
|
|
#define NGKNETCB_MODULE_MAJOR 122
|
|
|
|
/* set KNET_CB_DEBUG for debug info */
|
|
#define KNET_CB_DEBUG
|
|
|
|
/* These below need to match incoming enum values */
|
|
#define FILTER_TAG_STRIP 0
|
|
#define FILTER_TAG_KEEP 1
|
|
#define FILTER_TAG_ORIGINAL 2
|
|
|
|
/* Maintain tag strip statistics */
|
|
struct strip_stats_s {
|
|
unsigned long stripped; /* Number of packets that have been stripped */
|
|
unsigned long checked;
|
|
unsigned long skipped;
|
|
};
|
|
|
|
static struct strip_stats_s strip_stats;
|
|
static unsigned int rx_count = 0;
|
|
|
|
/* Local function prototypes */
|
|
static void strip_vlan_tag(struct sk_buff *skb);
|
|
|
|
/* Remove VLAN tag for select TPIDs */
|
|
static void
|
|
strip_vlan_tag(struct sk_buff *skb)
|
|
{
|
|
uint16_t vlan_proto;
|
|
uint8_t *pkt = skb->data;
|
|
|
|
vlan_proto = (uint16_t) ((pkt[12] << 8) | pkt[13]);
|
|
if ((vlan_proto == 0x8100) || (vlan_proto == 0x88a8) || (vlan_proto == 0x9100)) {
|
|
/* Move first 12 bytes of packet back by 4 */
|
|
memmove(&skb->data[4], skb->data, 12);
|
|
skb_pull(skb, 4); /* Remove 4 bytes from start of buffer */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The function get_tag_status() returns the tag status.
|
|
* 0 = Untagged
|
|
* 1 = Single inner-tag
|
|
* 2 = Single outer-tag
|
|
* 3 = Double tagged.
|
|
* -1 = Unsupported type
|
|
*/
|
|
static int
|
|
get_tag_status(uint32_t dev_type, void *meta)
|
|
{
|
|
uint32_t *valptr;
|
|
uint32_t fd_index;
|
|
uint32_t outer_l2_hdr;
|
|
int tag_status = -1;
|
|
|
|
if ((dev_type == 0xb880) || (dev_type == 0xb780))
|
|
{
|
|
/* Field BCM_PKTIO_RXPMD_MATCH_ID_LO has tag status in RX PMD */
|
|
fd_index = 2;
|
|
valptr = (uint32_t *)meta;
|
|
outer_l2_hdr = (valptr[fd_index] >> ((dev_type == 0xb780) ? 2 : 1) & 0xFF);
|
|
|
|
if (outer_l2_hdr & 0x1) {
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1) {
|
|
printk(" L2 Header Present\n");
|
|
}
|
|
#endif
|
|
tag_status = 0;
|
|
if (outer_l2_hdr & 0x4) {
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1) {
|
|
printk(" SNAP/LLC\n");
|
|
}
|
|
#endif
|
|
tag_status = 0;
|
|
}
|
|
if (outer_l2_hdr & 0x10) {
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1) {
|
|
printk(" Outer Tagged\n");
|
|
}
|
|
#endif
|
|
tag_status = 2;
|
|
if (outer_l2_hdr & 0x20) {
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1) {
|
|
printk(" Double Tagged\n");
|
|
}
|
|
#endif
|
|
tag_status = 3;
|
|
}
|
|
}
|
|
else if (outer_l2_hdr & 0x20) {
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1) {
|
|
printk(" Inner Tagged\n");
|
|
}
|
|
#endif
|
|
tag_status = 1;
|
|
}
|
|
}
|
|
}
|
|
else if ((dev_type == 0xb990)|| (dev_type == 0xb996))
|
|
{
|
|
fd_index = 9;
|
|
valptr = (uint32_t *)meta;
|
|
/* On TH4, outer_l2_header means INCOMING_TAG_STATUS.
|
|
* TH4 only supports single tagging, so if TAG_STATUS
|
|
* says there's a tag, then we don't want to strip.
|
|
* Otherwise, we do.
|
|
*/
|
|
outer_l2_hdr = (valptr[fd_index] >> 13) & 3;
|
|
|
|
if (outer_l2_hdr)
|
|
{
|
|
tag_status = 2;
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1)
|
|
{
|
|
printk(" Incoming frame tagged\n");
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
tag_status = 0;
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1)
|
|
{
|
|
printk(" Incoming frame untagged\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1) {
|
|
printk("%s; Device Type: %d; tag status: %d\n", __func__, dev_type, tag_status);
|
|
}
|
|
#endif
|
|
return tag_status;
|
|
}
|
|
|
|
#ifdef KNET_CB_DEBUG
|
|
static void
|
|
dump_buffer(uint8_t * data, int size)
|
|
{
|
|
const char *const to_hex = "0123456789ABCDEF";
|
|
int i;
|
|
char buffer[128];
|
|
char *buffer_ptr;
|
|
int addr = 0;
|
|
|
|
buffer_ptr = buffer;
|
|
for (i = 0; i < size; i++) {
|
|
*buffer_ptr++ = ' ';
|
|
*buffer_ptr++ = to_hex[(data[i] >> 4) & 0xF];
|
|
*buffer_ptr++ = to_hex[data[i] & 0xF];
|
|
if (((i % 16) == 15) || (i == size - 1)) {
|
|
*buffer_ptr = '\0';
|
|
buffer_ptr = buffer;
|
|
printk(KERN_INFO "%04X %s\n", addr, buffer);
|
|
addr = i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
show_pmd(uint8_t *pmd, int len)
|
|
{
|
|
if (debug & 0x1) {
|
|
printk("PMD (%d bytes):\n", len);
|
|
dump_buffer(pmd, len);
|
|
}
|
|
}
|
|
|
|
static void
|
|
show_mac(uint8_t *pkt)
|
|
{
|
|
if (debug & 0x1) {
|
|
printk("DMAC=%02X:%02X:%02X:%02X:%02X:%02X\n",
|
|
pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static struct sk_buff *
|
|
strip_tag_rx_cb(struct sk_buff *skb)
|
|
{
|
|
const struct ngknet_callback_desc *cbd = NGKNET_SKB_CB(skb);
|
|
const struct ngknet_private *priv = cbd->priv;
|
|
int rcpu_mode = 0;
|
|
int tag_status;
|
|
|
|
rcpu_mode = (priv->flags & NGKNET_NETIF_F_RCPU_ENCAP)? 1 : 0;
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1)
|
|
{
|
|
printk(KERN_INFO
|
|
"\n%4u --------------------------------------------------------------------------------\n",
|
|
rx_count);
|
|
printk(KERN_INFO
|
|
"RX KNET callback: dev_no=%1d; dev_id=0x%04X; type_str=%4s; RCPU: %3s \n",
|
|
cbd->dev_no, cbd->dev_id, cbd->type_str, rcpu_mode ? "yes" : "no");
|
|
printk(KERN_INFO " pkt_len=%4d; pmd_len=%2d; SKB len: %4d\n",
|
|
cbd->pkt_len, cbd->pmd_len, skb->len);
|
|
if (cbd->filt) {
|
|
printk(KERN_INFO "Filter user data: 0x%08x\n",
|
|
*(uint32_t *) cbd->filt->user_data);
|
|
}
|
|
printk(KERN_INFO "Before SKB (%d bytes):\n", skb->len);
|
|
dump_buffer(skb->data, skb->len);
|
|
printk("rx_cb for dev %d: id 0x%x, %s\n", cbd->dev_no, cbd->dev_id, cbd->type_str);
|
|
printk("netif user data: 0x%08x\n", *(uint32_t *)cbd->priv->user_data);
|
|
show_pmd(cbd->pmd, cbd->pmd_len);
|
|
if (rcpu_mode) {
|
|
const int RCPU_header_len = PKT_HDR_SIZE + cbd->pmd_len;
|
|
const int payload_len = skb->len - RCPU_header_len;
|
|
unsigned char *payload_start = skb->data + payload_len;
|
|
|
|
printk(KERN_INFO "Packet Payload (%d bytes):\n", payload_len);
|
|
dump_buffer(payload_start, payload_len);
|
|
} else {
|
|
printk(KERN_INFO "Packet (%d bytes):\n", cbd->pkt_len);
|
|
dump_buffer(skb->data, cbd->pkt_len);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((!rcpu_mode) && (cbd->filt)) {
|
|
if (FILTER_TAG_ORIGINAL == cbd->filt->user_data[0]) {
|
|
tag_status = get_tag_status(cbd->dev_id, (void *)cbd->pmd);
|
|
if (tag_status < 0) {
|
|
strip_stats.skipped++;
|
|
goto _strip_tag_rx_cb_exit;
|
|
}
|
|
strip_stats.checked++;
|
|
if (tag_status < 2) {
|
|
strip_stats.stripped++;
|
|
strip_vlan_tag(skb);
|
|
}
|
|
}
|
|
}
|
|
_strip_tag_rx_cb_exit:
|
|
#ifdef KNET_CB_DEBUG
|
|
if (debug & 0x1) {
|
|
printk(KERN_INFO "After SKB (%d bytes):\n", skb->len);
|
|
dump_buffer(skb->data, skb->len);
|
|
printk(KERN_INFO
|
|
"\n%4u --------------------------------------------------------------------------------\n",
|
|
rx_count++);
|
|
}
|
|
#endif
|
|
return skb;
|
|
}
|
|
|
|
static struct sk_buff *
|
|
strip_tag_tx_cb(struct sk_buff *skb)
|
|
{
|
|
#ifdef KNET_CB_DEBUG
|
|
struct ngknet_callback_desc *cbd = NGKNET_SKB_CB(skb);
|
|
|
|
if (debug & 0x1) {
|
|
printk("tx_cb for dev %d: %s\n", cbd->dev_no, cbd->type_str);
|
|
}
|
|
show_pmd(cbd->pmd, cbd->pmd_len);
|
|
show_mac(cbd->pmd + cbd->pmd_len);
|
|
#endif
|
|
return skb;
|
|
}
|
|
|
|
static struct sk_buff *
|
|
ngknet_rx_cb(struct sk_buff *skb)
|
|
{
|
|
skb = strip_tag_rx_cb(skb);
|
|
#ifdef PSAMPLE_SUPPORT
|
|
skb = psample_rx_cb(skb);
|
|
#endif
|
|
return skb;
|
|
}
|
|
|
|
static struct sk_buff *
|
|
ngknet_tx_cb(struct sk_buff *skb)
|
|
{
|
|
skb = strip_tag_tx_cb(skb);
|
|
return skb;
|
|
}
|
|
|
|
static int
|
|
ngknet_netif_create_cb(struct net_device *dev)
|
|
{
|
|
int retv = 0;
|
|
#ifdef PSAMPLE_SUPPORT
|
|
retv = psample_netif_create_cb(dev);
|
|
#endif
|
|
return retv;
|
|
}
|
|
|
|
static int
|
|
ngknet_netif_destroy_cb(struct net_device *dev)
|
|
{
|
|
int retv = 0;
|
|
#ifdef PSAMPLE_SUPPORT
|
|
retv = psample_netif_destroy_cb(dev);
|
|
#endif
|
|
return retv;
|
|
}
|
|
|
|
/*!
|
|
* Generic module functions
|
|
*/
|
|
static int
|
|
ngknetcb_show(struct seq_file *m, void *v)
|
|
{
|
|
seq_printf(m, "Broadcom Linux NGKNET Callback: Untagged VLAN Stripper\n");
|
|
seq_printf(m, " %lu stripped packets\n", strip_stats.stripped);
|
|
seq_printf(m, " %lu packets checked\n", strip_stats.checked);
|
|
seq_printf(m, " %lu packets skipped\n", strip_stats.skipped);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ngknetcb_open(struct inode *inode, struct file *filp)
|
|
{
|
|
return single_open(filp, ngknetcb_show, NULL);
|
|
}
|
|
|
|
static int
|
|
ngknetcb_release(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t
|
|
ngknetcb_write(struct file *file, const char *buf,
|
|
size_t count, loff_t *loff)
|
|
{
|
|
memset(&strip_stats, 0, sizeof(strip_stats));
|
|
printk("Cleared NGKNET callback stats\n");
|
|
return count;
|
|
}
|
|
|
|
static long
|
|
ngknetcb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ngknetcb_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static struct file_operations ngknetcb_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ngknetcb_open,
|
|
.read = seq_read,
|
|
.write = ngknetcb_write,
|
|
.llseek = seq_lseek,
|
|
.release = ngknetcb_release,
|
|
.unlocked_ioctl = ngknetcb_ioctl,
|
|
.compat_ioctl = ngknetcb_ioctl,
|
|
.mmap = ngknetcb_mmap,
|
|
};
|
|
|
|
static int __init
|
|
ngknetcb_init_module(void)
|
|
{
|
|
int rv;
|
|
struct proc_dir_entry *entry = NULL;
|
|
|
|
rv = register_chrdev(NGKNETCB_MODULE_MAJOR, NGKNETCB_MODULE_NAME, &ngknetcb_fops);
|
|
if (rv < 0) {
|
|
printk(KERN_WARNING "%s: can't get major %d\n",
|
|
NGKNETCB_MODULE_NAME, NGKNETCB_MODULE_MAJOR);
|
|
return rv;
|
|
}
|
|
|
|
PROC_CREATE(entry, NGKNETCB_MODULE_NAME, 0666, NULL, &ngknetcb_fops);
|
|
if (entry == NULL) {
|
|
printk(KERN_ERR "%s: proc_mkdir failed\n",
|
|
NGKNETCB_MODULE_NAME);
|
|
}
|
|
|
|
ngknet_rx_cb_register(ngknet_rx_cb);
|
|
ngknet_tx_cb_register(ngknet_tx_cb);
|
|
|
|
#ifdef PSAMPLE_SUPPORT
|
|
psample_init();
|
|
#endif
|
|
|
|
ngknet_netif_create_cb_register(ngknet_netif_create_cb);
|
|
ngknet_netif_destroy_cb_register(ngknet_netif_destroy_cb);
|
|
return 0;
|
|
}
|
|
|
|
static void __exit
|
|
ngknetcb_exit_module(void)
|
|
{
|
|
ngknet_netif_create_cb_unregister(ngknet_netif_create_cb);
|
|
ngknet_netif_destroy_cb_unregister(ngknet_netif_destroy_cb);
|
|
|
|
#ifdef PSAMPLE_SUPPORT
|
|
psample_cleanup();
|
|
#endif
|
|
|
|
ngknet_rx_cb_unregister(ngknet_rx_cb);
|
|
ngknet_tx_cb_unregister(ngknet_tx_cb);
|
|
|
|
remove_proc_entry(NGKNETCB_MODULE_NAME, NULL);
|
|
|
|
unregister_chrdev(NGKNETCB_MODULE_MAJOR, NGKNETCB_MODULE_NAME);
|
|
}
|
|
|
|
module_init(ngknetcb_init_module);
|
|
module_exit(ngknetcb_exit_module);
|