4f9569132f
Update the build rules in saibcm-modules to use the 5.10 kernel instead of searching for the 4.19 kernel. In addition, some code changes were done to get it to compile. The main categories of such changes are as follows: * For /proc files, `struct file_operations` has been replaced with `struct proc_ops`. * Y2038 changes to use the new APIs, since `do_gettimeofday()` is no longer available. * Minor changes in how external kernel module symbols are read by modpost. Signed-off-by: Saikrishna Arcot <sarcot@microsoft.com>
463 lines
12 KiB
C
463 lines
12 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, uint32_t variant, void *meta)
|
|
{
|
|
uint32_t *valptr;
|
|
uint32_t fd_index;
|
|
uint32_t outer_l2_hdr;
|
|
int tag_status = -1;
|
|
uint32_t match_id_minbit = 1;
|
|
uint32_t outer_tag_match = 0x10;
|
|
|
|
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;
|
|
match_id_minbit = (dev_type == 0xb780) ? 2 : 1;
|
|
outer_l2_hdr = (valptr[fd_index] >> match_id_minbit & 0xFF);
|
|
outer_tag_match = ((dev_type == 0xb780 && variant == 1) ? 0x8 : 0x10);
|
|
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 & outer_tag_match) {
|
|
#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, cbd->filt->user_data[1],(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);
|
|
}
|
|
}
|
|
if (FILTER_TAG_STRIP == cbd->filt->user_data[0]) {
|
|
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 struct proc_ops ngknetcb_proc_ops = {
|
|
.proc_open = ngknetcb_open,
|
|
.proc_read = seq_read,
|
|
.proc_write = ngknetcb_write,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = ngknetcb_release,
|
|
.proc_ioctl = ngknetcb_ioctl,
|
|
.proc_compat_ioctl = ngknetcb_ioctl,
|
|
.proc_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_proc_ops);
|
|
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);
|