mirror of
https://github.com/qosmio/nss-packages.git
synced 2025-12-17 00:33:40 +00:00
wwan: Make rmnet-nss into a separate module
Allows the rmnet-nss module to be built independently of the Quectel driver. If `rmnet-nss` is selected, the Quectel drivers (wwan/mhi) will enable NSS offload support support. NOTE: NSS offload is completely broken on IPQ5018. Do NOT enable this module. The Quectel drivers and rmnet-nss module are compile and load tested (insmod) ONLY. There is NO guarantee or support outside of build errors. Signed-off-by: Sean Khan <datapronix@protonmail.com>
This commit is contained in:
parent
0dd2aa67ab
commit
b79e9b5989
@ -2,19 +2,19 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=pcie_mhi
|
||||
PKG_VERSION:=1.3.8
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=gc-sections lto
|
||||
|
||||
define KernelPackage/pcie_mhi
|
||||
SUBMENU:=Network Devices
|
||||
TITLE:=Kernel PCIe driver for MHI device
|
||||
DEPENDS:=@(TARGET_qualcommax_ipq807x||TARGET_qualcommax_ipq50xx) \
|
||||
+pciids +pciutils +quectel-cm
|
||||
+pciids +pciutils +quectel-cm \
|
||||
+PACKAGE_kmod-rmnet-nss:kmod-rmnet-nss
|
||||
FILES:=$(PKG_BUILD_DIR)/pcie_mhi.ko
|
||||
AUTOLOAD:=$(call AutoLoad,90,pcie_mhi)
|
||||
endef
|
||||
@ -24,11 +24,15 @@ define KernelPackage/pcie_mhi/description
|
||||
endef
|
||||
|
||||
EXTRA_CFLAGS+= \
|
||||
-I$(STAGING_DIR)/usr/include/qca-nss-drv \
|
||||
-Wno-unused-function \
|
||||
-Wno-missing-prototypes \
|
||||
-Wno-missing-declarations
|
||||
|
||||
ifneq ($(CONFIG_PACKAGE_kmod-rmnet-nss),)
|
||||
EXTRA_CFLAGS += -I$(STAGING_DIR)/usr/include/qca-nss-rmnet
|
||||
EXTRA_CFLAGS += -DCONFIG_QCA_NSS_DRV
|
||||
endif
|
||||
|
||||
define Build/Compile
|
||||
+$(KERNEL_MAKE) EXTRA_CFLAGS="$(EXTRA_CFLAGS)" M="$(PKG_BUILD_DIR)" \
|
||||
modules
|
||||
|
||||
@ -31,8 +31,6 @@
|
||||
#include <net/tcp.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
|
||||
#include <rmnet_nss.h>
|
||||
|
||||
//#define CONFIG_IPQ5018_RATE_CONTROL //Only used with spf11.5 for IPQ5018
|
||||
#if defined(CONFIG_IPQ5018_RATE_CONTROL)
|
||||
//#include <linux/jiffies.h>
|
||||
@ -59,19 +57,26 @@ static bool netdev_is_rx_handler_busy(struct net_device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
|
||||
#ifdef CONFIG_QCA_NSS_DRV
|
||||
#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018) || defined(CONFIG_PINCTRL_IPQ8074)
|
||||
//#ifdef CONFIG_RMNET_DATA //spf12.x have no macro defined, just for spf11.x
|
||||
#define CONFIG_QCA_NSS_DRV
|
||||
#define CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY
|
||||
#include <rmnet_nss.h>
|
||||
//#ifdef CONFIG_RMNET_DATA //spf12.x have no macro defined, just for spf11.x
|
||||
/* define at qca/src/linux-4.4/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c */ //for spf11.x
|
||||
/* define at qsdk/qca/src/datarmnet/core/rmnet_config.c */ //for spf12.x
|
||||
/* set at qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c */
|
||||
/* need add DEPENDS:= kmod-rmnet-core in feeds/makefile */
|
||||
extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
|
||||
//#endif
|
||||
#endif
|
||||
#else
|
||||
struct rmnet_nss_cb {
|
||||
int (*nss_create)(void *dev);
|
||||
int (*nss_free)(void *dev);
|
||||
int (*nss_tx)(void *skb);
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
|
||||
|
||||
int mhi_netdev_use_xfer_type_dma(unsigned chan)
|
||||
{
|
||||
|
||||
@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=qmi_wwan_q
|
||||
PKG_VERSION:=1.2.9
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
@ -15,17 +15,21 @@ define KernelPackage/qmi_wwan_q
|
||||
DEPENDS:=@(TARGET_qualcommax_ipq807x||TARGET_qualcommax_ipq50xx) \
|
||||
+kmod-usb-net \
|
||||
+kmod-usb-wdm \
|
||||
+kmod-qca-nss-drv \
|
||||
+@NSS_DRV_RMNET_ENABLE \
|
||||
+@NSS_DRV_C2C_ENABLE
|
||||
FILES:=$(PKG_BUILD_DIR)/qmi_wwan_q.ko \
|
||||
$(PKG_BUILD_DIR)/rmnet_nss.ko
|
||||
AUTOLOAD:=$(call AutoLoad,42,rmnet_nss) \
|
||||
$(call AutoLoad,81,qmi_wwan_q)
|
||||
+PACKAGE_kmod-rmnet-nss:kmod-rmnet-nss
|
||||
FILES:=$(PKG_BUILD_DIR)/qmi_wwan_q.ko
|
||||
AUTOLOAD:=$(call AutoLoad,81,qmi_wwan_q)
|
||||
endef
|
||||
|
||||
|
||||
EXTRA_CFLAGS+= \
|
||||
-I$(STAGING_DIR)/usr/include/qca-nss-drv
|
||||
-Wno-unused-function \
|
||||
-Wno-missing-prototypes \
|
||||
-Wno-missing-declarations
|
||||
|
||||
ifneq ($(CONFIG_PACKAGE_kmod-rmnet-nss),)
|
||||
EXTRA_CFLAGS += -I$(STAGING_DIR)/usr/include/qca-nss-rmnet
|
||||
EXTRA_CFLAGS += -DCONFIG_QCA_NSS_DRV
|
||||
endif
|
||||
|
||||
define Build/Compile
|
||||
+$(KERNEL_MAKE) EXTRA_CFLAGS="$(EXTRA_CFLAGS)" M="$(PKG_BUILD_DIR)" \
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
obj-m += rmnet_nss.o qmi_wwan_q.o
|
||||
obj-m += qmi_wwan_q.o
|
||||
|
||||
PWD := $(shell pwd)
|
||||
|
||||
|
||||
@ -33,8 +33,6 @@
|
||||
#include <linux/usb/usbnet.h>
|
||||
#include <linux/usb/cdc-wdm.h>
|
||||
|
||||
#include "rmnet_nss.h"
|
||||
|
||||
#ifndef ETH_P_MAP
|
||||
#define ETH_P_MAP 0xDA1A
|
||||
#endif
|
||||
@ -48,22 +46,25 @@
|
||||
#define ARPHRD_RAWIP ARPHRD_NONE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PINCTRL_IPQ807x
|
||||
#define CONFIG_QCA_NSS_DRV
|
||||
//#define CONFIG_QCA_NSS_PACKET_FILTER
|
||||
#endif
|
||||
|
||||
static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
|
||||
#ifdef CONFIG_QCA_NSS_DRV
|
||||
#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018) || defined(CONFIG_PINCTRL_IPQ8074)
|
||||
//#ifdef CONFIG_RMNET_DATA //spf12.x none, not effect for spf11.x
|
||||
#define CONFIG_QCA_NSS_DRV
|
||||
/* define at qsdk/qca/src/linux-4.4/net/rmnet_data/rmnet_data_main.c */ //for spf11.x
|
||||
#include <rmnet_nss.h>
|
||||
//#ifdef CONFIG_RMNET_DATA //spf12.x have no macro defined, just for spf11.x
|
||||
/* define at qca/src/linux-4.4/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c */ //for spf11.x
|
||||
/* define at qsdk/qca/src/datarmnet/core/rmnet_config.c */ //for spf12.x
|
||||
/* set at qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c */
|
||||
/* need add DEPENDS:= kmod-rmnet-core in feeds/makefile */
|
||||
extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
|
||||
//#endif
|
||||
#endif
|
||||
#else
|
||||
struct rmnet_nss_cb {
|
||||
int (*nss_create)(void *dev);
|
||||
int (*nss_free)(void *dev);
|
||||
int (*nss_tx)(void *skb);
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
|
||||
|
||||
/* This driver supports wwan (3G/LTE/?) devices using a vendor
|
||||
* specific management protocol called Qualcomm MSM Interface (QMI) -
|
||||
|
||||
38
wwan/driver/rmnet-nss/Makefile
Normal file
38
wwan/driver/rmnet-nss/Makefile
Normal file
@ -0,0 +1,38 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=rmnet-nss
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
define KernelPackage/rmnet-nss
|
||||
SECTION:=kernel
|
||||
CATEGORY:=Kernel modules
|
||||
SUBMENU:=Network Devices
|
||||
TITLE:=RmNet QCA NSS support
|
||||
DEPENDS:=@(TARGET_qualcommax_ipq807x||TARGET_qualcommax_ipq50xx) \
|
||||
+kmod-qca-nss-drv \
|
||||
+@NSS_DRV_RMNET_ENABLE \
|
||||
+@NSS_DRV_C2C_ENABLE
|
||||
FILES:=$(PKG_BUILD_DIR)/rmnet_nss.ko
|
||||
AUTOLOAD:=$(call AutoLoad,42,rmnet_nss)
|
||||
endef
|
||||
|
||||
EXTRA_CFLAGS+= \
|
||||
-I$(STAGING_DIR)/usr/include/qca-nss-drv
|
||||
|
||||
define Build/InstallDev
|
||||
mkdir -p $(1)/usr/include/qca-nss-rmnet
|
||||
$(CP) $(PKG_BUILD_DIR)/rmnet_nss.h $(1)/usr/include/qca-nss-rmnet/rmnet_nss.h
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
+$(KERNEL_MAKE) EXTRA_CFLAGS="$(EXTRA_CFLAGS)" M="$(PKG_BUILD_DIR)" \
|
||||
modules
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,rmnet-nss))
|
||||
32
wwan/driver/rmnet-nss/src/Makefile
Normal file
32
wwan/driver/rmnet-nss/src/Makefile
Normal file
@ -0,0 +1,32 @@
|
||||
obj-m += rmnet_nss.o
|
||||
|
||||
KDIR ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
KERNEL_MAKE_OPTS := -C $(KDIR) M=$(CURDIR)
|
||||
ifneq ($(ARCH),)
|
||||
KERNEL_MAKE_OPTS += ARCH=$(ARCH)
|
||||
endif
|
||||
ifneq ($(CROSS_COMPILE),)
|
||||
KERNEL_MAKE_OPTS += CROSS_COMPILE=$(CROSS_COMPILE)
|
||||
endif
|
||||
ifneq ($(INSTALL_MOD_PATH),)
|
||||
KERNEL_MAKE_OPTS += INSTALL_MOD_PATH=$(INSTALL_MOD_PATH)
|
||||
endif
|
||||
|
||||
PWD := $(shell pwd)
|
||||
|
||||
default:
|
||||
$(MAKE) $(KERNEL_MAKE_OPTS) modules
|
||||
|
||||
install: modules_install
|
||||
|
||||
modules_install:
|
||||
$(MAKE) $(KERNEL_MAKE_OPTS) modules_install
|
||||
|
||||
install: default
|
||||
install -m 644 $(PWD)/*.ko /lib/modules/$(shell uname -r)/kernel/drivers/net/usb/
|
||||
depmod
|
||||
|
||||
clean:
|
||||
rm -rf *~ .tmp_versions modules.order Module.symvers
|
||||
find . -type f -name "*~" -o -name "*.o" -o -name "*.ko" -o -name "*.cmd" -o -name "*.mod.c" | xargs rm -rf
|
||||
494
wwan/driver/rmnet-nss/src/rmnet_nss.c
Normal file
494
wwan/driver/rmnet-nss/src/rmnet_nss.c
Normal file
@ -0,0 +1,494 @@
|
||||
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/version.h>
|
||||
#include <nss_api_if.h>
|
||||
|
||||
#include "rmnet_nss.h"
|
||||
|
||||
#define RMNET_NSS_HASH_BITS 8
|
||||
#define hash_add_ptr(table, node, key) \
|
||||
hlist_add_head(node, &table[hash_ptr(key, HASH_BITS(table))])
|
||||
|
||||
static DEFINE_HASHTABLE(rmnet_nss_ctx_hashtable, RMNET_NSS_HASH_BITS);
|
||||
|
||||
struct rmnet_nss_cb *rmnet_nss_callbacks;
|
||||
EXPORT_SYMBOL(rmnet_nss_callbacks);
|
||||
|
||||
struct rmnet_nss_ctx {
|
||||
struct hlist_node hnode;
|
||||
struct net_device *rmnet_dev;
|
||||
struct nss_rmnet_rx_handle *nss_ctx;
|
||||
};
|
||||
|
||||
enum __rmnet_nss_stat {
|
||||
RMNET_NSS_RX_ETH,
|
||||
RMNET_NSS_RX_FAIL,
|
||||
RMNET_NSS_RX_NON_ETH,
|
||||
RMNET_NSS_RX_BUSY,
|
||||
RMNET_NSS_TX_NO_CTX,
|
||||
RMNET_NSS_TX_SUCCESS,
|
||||
RMNET_NSS_TX_FAIL,
|
||||
RMNET_NSS_TX_NONLINEAR,
|
||||
RMNET_NSS_TX_BAD_IP,
|
||||
RMNET_NSS_EXCEPTIONS,
|
||||
RMNET_NSS_EX_BAD_HDR,
|
||||
RMNET_NSS_EX_BAD_IP,
|
||||
RMNET_NSS_EX_SUCCESS,
|
||||
RMNET_NSS_TX_BAD_FRAGS,
|
||||
RMNET_NSS_TX_LINEARIZE_FAILS,
|
||||
RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
|
||||
RMNET_NSS_TX_BUSY_LOOP,
|
||||
RMNET_NSS_NUM_STATS,
|
||||
};
|
||||
|
||||
static unsigned long rmnet_nss_stats[RMNET_NSS_NUM_STATS];
|
||||
extern void qmi_rmnet_mark_skb(struct net_device *dev, struct sk_buff *skb);
|
||||
static void (*rmnet_mark_skb)(struct net_device *dev, struct sk_buff *skb);
|
||||
|
||||
#define RMNET_NSS_STAT(name, counter, desc) \
|
||||
module_param_named(name, rmnet_nss_stats[counter], ulong, 0444); \
|
||||
MODULE_PARM_DESC(name, desc)
|
||||
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_ethernet, RMNET_NSS_RX_ETH,
|
||||
"Number of Ethernet headers successfully removed");
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_fail, RMNET_NSS_RX_FAIL,
|
||||
"Number of Ethernet headers that could not be removed");
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_non_ethernet, RMNET_NSS_RX_NON_ETH,
|
||||
"Number of non-Ethernet packets received");
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_busy, RMNET_NSS_RX_BUSY,
|
||||
"Number of packets dropped decause rmnet_data device was busy");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_slow, RMNET_NSS_TX_NO_CTX,
|
||||
"Number of packets sent over non-NSS-accelerated rmnet device");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_fast, RMNET_NSS_TX_SUCCESS,
|
||||
"Number of packets sent over NSS-accelerated rmnet device");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_fail, RMNET_NSS_TX_FAIL,
|
||||
"Number of packets that NSS could not transmit");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_nonlinear, RMNET_NSS_TX_NONLINEAR,
|
||||
"Number of non linear sent over NSS-accelerated rmnet device");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_invalid_ip, RMNET_NSS_TX_BAD_IP,
|
||||
"Number of ingress packets with invalid IP headers");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_invalid_frags, RMNET_NSS_TX_BAD_FRAGS,
|
||||
"Number of ingress packets with invalid frag format");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_linearize_fail, RMNET_NSS_TX_LINEARIZE_FAILS,
|
||||
"Number of ingress packets where linearize in tx fails");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_exceptions, RMNET_NSS_EXCEPTIONS,
|
||||
"Number of times our DL exception handler was invoked");
|
||||
RMNET_NSS_STAT(rmnet_nss_exception_non_ethernet, RMNET_NSS_EX_BAD_HDR,
|
||||
"Number of non-Ethernet exception packets");
|
||||
RMNET_NSS_STAT(rmnet_nss_exception_invalid_ip, RMNET_NSS_EX_BAD_IP,
|
||||
"Number of exception packets with invalid IP headers");
|
||||
RMNET_NSS_STAT(rmnet_nss_exception_success, RMNET_NSS_EX_SUCCESS,
|
||||
"Number of exception packets handled successfully");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_non_zero_headlen_frags, RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
|
||||
"Number of packets with non zero headlen");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_busy_loop, RMNET_NSS_TX_BUSY_LOOP,
|
||||
"Number of times tx packets busy looped");
|
||||
|
||||
static void rmnet_nss_inc_stat(enum __rmnet_nss_stat stat)
|
||||
{
|
||||
if (stat >= 0 && stat < RMNET_NSS_NUM_STATS)
|
||||
rmnet_nss_stats[stat]++;
|
||||
}
|
||||
|
||||
static struct rmnet_nss_ctx *rmnet_nss_find_ctx(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
struct hlist_head *bucket;
|
||||
u32 hash;
|
||||
|
||||
hash = hash_ptr(dev, HASH_BITS(rmnet_nss_ctx_hashtable));
|
||||
bucket = &rmnet_nss_ctx_hashtable[hash];
|
||||
hlist_for_each_entry(ctx, bucket, hnode) {
|
||||
if (ctx->rmnet_dev == dev)
|
||||
return ctx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rmnet_nss_free_ctx(struct rmnet_nss_ctx *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
hash_del(&ctx->hnode);
|
||||
nss_rmnet_rx_xmit_callback_unregister(ctx->nss_ctx);
|
||||
nss_rmnet_rx_destroy_sync(ctx->nss_ctx);
|
||||
kfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pull off an ethernet header, if possible */
|
||||
static int rmnet_nss_ethhdr_pull(struct sk_buff *skb)
|
||||
{
|
||||
if (!skb->protocol || skb->protocol == htons(ETH_P_802_3)) {
|
||||
void *ret = skb_pull(skb, sizeof(struct ethhdr));
|
||||
|
||||
rmnet_nss_inc_stat((ret) ? RMNET_NSS_RX_ETH :
|
||||
RMNET_NSS_RX_FAIL);
|
||||
return !ret;
|
||||
}
|
||||
|
||||
rmnet_nss_inc_stat(RMNET_NSS_RX_NON_ETH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rmnet_nss_handle_non_zero_headlen(struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
u8 transport;
|
||||
|
||||
if (skb_headlen(skb) < sizeof(struct iphdr)){
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iph = (struct iphdr *)skb->data;
|
||||
|
||||
if (iph->version == 4) {
|
||||
transport = iph->protocol;
|
||||
} else if (iph->version == 6) {
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)iph;
|
||||
transport = ip6h->nexthdr;
|
||||
} else {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Assumption: required headers are copied in case of TCP/UDP by SFE */
|
||||
/* In case of TCP/UDP where there are no IP extension headers, the assumption is that SFE copied the IP and Transport header */
|
||||
|
||||
if (transport != IPPROTO_TCP && transport != IPPROTO_UDP) {
|
||||
if (skb_linearize(skb)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_LINEARIZE_FAILS);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
else if ((transport == IPPROTO_TCP && skb_headlen(skb) < 40) || (transport == IPPROTO_UDP && skb_headlen(skb) < 28)) {
|
||||
pr_err_ratelimited("rmnet_nss: error: Partial copy of headers\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy headers to linear section for non linear packets */
|
||||
static int rmnet_nss_adjust_header(struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
skb_frag_t *frag;
|
||||
int bytes = 0;
|
||||
u8 transport;
|
||||
|
||||
if (skb_shinfo(skb)->nr_frags != 1) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (skb_headlen(skb)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS);
|
||||
return rmnet_nss_handle_non_zero_headlen(skb);
|
||||
}
|
||||
|
||||
frag = &skb_shinfo(skb)->frags[0];
|
||||
|
||||
iph = (struct iphdr *)(skb_frag_address(frag));
|
||||
|
||||
if (iph->version == 4) {
|
||||
bytes = iph->ihl*4;
|
||||
transport = iph->protocol;
|
||||
} else if (iph->version == 6) {
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)iph;
|
||||
|
||||
bytes = sizeof(struct ipv6hdr);
|
||||
/* Dont have to account for extension headers yet */
|
||||
transport = ip6h->nexthdr;
|
||||
} else {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (transport == IPPROTO_TCP) {
|
||||
struct tcphdr *th;
|
||||
|
||||
th = (struct tcphdr *)((u8 *)iph + bytes);
|
||||
bytes += th->doff * 4;
|
||||
} else if (transport == IPPROTO_UDP) {
|
||||
bytes += sizeof(struct udphdr);
|
||||
} else {
|
||||
/* cant do anything else here unfortunately so linearize */
|
||||
if (skb_linearize(skb)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_LINEARIZE_FAILS);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes > skb_frag_size(frag)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
skb_push(skb, bytes);
|
||||
memcpy(skb->data, iph, bytes);
|
||||
|
||||
/* subtract to account for skb_push */
|
||||
skb->len -= bytes;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0))
|
||||
frag->offset += bytes;
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
|
||||
frag->bv_offset += bytes;
|
||||
#else
|
||||
frag->page_offset += bytes;
|
||||
#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(5, 4, 0)) */
|
||||
skb_frag_size_sub(frag, bytes);
|
||||
|
||||
/* subtract to account for skb_frag_size_sub */
|
||||
skb->data_len -= bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called by NSS in the DL exception case.
|
||||
* Since the packet cannot be sent over the accelerated path, we need to
|
||||
* handle it. Remove the ethernet header and pass it onward to the stack
|
||||
* if possible.
|
||||
*/
|
||||
static void rmnet_nss_receive(struct net_device *dev, struct sk_buff *skb,
|
||||
struct napi_struct *napi)
|
||||
{
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EXCEPTIONS);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
if (rmnet_nss_ethhdr_pull(skb)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_HDR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* reset header pointers */
|
||||
skb_reset_transport_header(skb);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_mac_header(skb);
|
||||
|
||||
/* reset packet type */
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
|
||||
skb->dev = dev;
|
||||
|
||||
/* reset protocol type */
|
||||
switch (skb->data[0] & 0xF0) {
|
||||
case 0x40:
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
break;
|
||||
case 0x60:
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
break;
|
||||
default:
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_IP);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EX_SUCCESS);
|
||||
|
||||
/* Set this so that we dont loop around netif_receive_skb */
|
||||
|
||||
skb->cb[0] = 1;
|
||||
|
||||
netif_receive_skb(skb);
|
||||
return;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* Main downlink handler
|
||||
* Looks up NSS contex associated with the device. If the context is found,
|
||||
* we add a dummy ethernet header with the approriate protocol field set,
|
||||
* the pass the packet off to NSS for hardware acceleration.
|
||||
*/
|
||||
static int rmnet_nss_tx(struct sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
struct net_device *dev = skb->dev;
|
||||
nss_tx_status_t rc;
|
||||
unsigned int len;
|
||||
u8 version;
|
||||
|
||||
if (skb_is_nonlinear(skb)) {
|
||||
if (rmnet_nss_adjust_header(skb))
|
||||
goto fail;
|
||||
else
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_NONLINEAR);
|
||||
}
|
||||
|
||||
version = ((struct iphdr *)skb->data)->version;
|
||||
|
||||
ctx = rmnet_nss_find_ctx(dev);
|
||||
if (!ctx) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_NO_CTX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
eth = (struct ethhdr *)skb_push(skb, sizeof(*eth));
|
||||
memset(eth->h_dest, 0, ETH_ALEN);
|
||||
memset(eth->h_source, 0, ETH_ALEN);
|
||||
if (version == 4) {
|
||||
eth->h_proto = htons(ETH_P_IP);
|
||||
} else if (version == 6) {
|
||||
eth->h_proto = htons(ETH_P_IPV6);
|
||||
} else {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
skb->protocol = htons(ETH_P_802_3);
|
||||
/* Get length including ethhdr */
|
||||
len = skb->len;
|
||||
|
||||
transmit:
|
||||
rc = nss_rmnet_rx_tx_buf(ctx->nss_ctx, skb);
|
||||
if (rc == NSS_TX_SUCCESS) {
|
||||
/* Increment rmnet_data device stats.
|
||||
* Don't call rmnet_data_vnd_rx_fixup() to do this, as
|
||||
* there's no guarantee the skb pointer is still valid.
|
||||
*/
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += len;
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_SUCCESS);
|
||||
return 0;
|
||||
} else if (rc == NSS_TX_FAILURE_QUEUE) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BUSY_LOOP);
|
||||
goto transmit;
|
||||
} else if (rc == NSS_TX_FAILURE_NOT_ENABLED) {
|
||||
/* New stats */
|
||||
rmnet_nss_receive(dev, skb, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fail:
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_FAIL);
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Called by NSS in the UL acceleration case.
|
||||
* We are guaranteed to have an ethernet packet here from the NSS hardware,
|
||||
* We need to pull the header off and invoke our ndo_start_xmit function
|
||||
* to handle transmitting the packet to the network stack.
|
||||
*/
|
||||
static void rmnet_nss_xmit(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
skb_pull(skb, sizeof(struct ethhdr));
|
||||
rmnet_nss_inc_stat(RMNET_NSS_RX_ETH);
|
||||
|
||||
/* Use top-half entry point for the netdev so that we enable QDisc support for RmNet redirect. */
|
||||
skb_reset_network_header(skb);
|
||||
skb->dev = dev;
|
||||
switch (skb->data[0] & 0xF0) {
|
||||
case 0x40:
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
break;
|
||||
case 0x60:
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (rmnet_mark_skb)
|
||||
rmnet_mark_skb(dev, skb);
|
||||
|
||||
rc = dev_queue_xmit(skb);
|
||||
if (unlikely(rc != 0)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_RX_BUSY);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and register an NSS context for an rmnet_data device */
|
||||
static int rmnet_nss_create_vnd(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->rmnet_dev = dev;
|
||||
ctx->nss_ctx = nss_rmnet_rx_create(dev);
|
||||
if (!ctx->nss_ctx) {
|
||||
kfree(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nss_rmnet_rx_register(ctx->nss_ctx, rmnet_nss_receive, dev);
|
||||
nss_rmnet_rx_xmit_callback_register(ctx->nss_ctx, rmnet_nss_xmit);
|
||||
hash_add_ptr(rmnet_nss_ctx_hashtable, &ctx->hnode, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unregister and destroy the NSS context for an rmnet_data device */
|
||||
static int rmnet_nss_free_vnd(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
|
||||
ctx = rmnet_nss_find_ctx(dev);
|
||||
rmnet_nss_free_ctx(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rmnet_nss_cb rmnet_nss = {
|
||||
.nss_create = rmnet_nss_create_vnd,
|
||||
.nss_free = rmnet_nss_free_vnd,
|
||||
.nss_tx = rmnet_nss_tx,
|
||||
};
|
||||
|
||||
static int __init rmnet_nss_init(void)
|
||||
{
|
||||
pr_err("%s(): initializing rmnet_nss\n", __func__);
|
||||
RCU_INIT_POINTER(rmnet_nss_callbacks, (struct rmnet_nss_cb *)&rmnet_nss);
|
||||
rmnet_mark_skb = symbol_get(qmi_rmnet_mark_skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rmnet_nss_exit(void)
|
||||
{
|
||||
struct hlist_node *tmp;
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
int bkt;
|
||||
|
||||
pr_err("%s(): exiting rmnet_nss\n", __func__);
|
||||
RCU_INIT_POINTER(rmnet_nss_callbacks, NULL);
|
||||
if (rmnet_mark_skb)
|
||||
symbol_put(qmi_rmnet_mark_skb);
|
||||
|
||||
/* Tear down all NSS contexts */
|
||||
hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode)
|
||||
rmnet_nss_free_ctx(ctx);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
module_init(rmnet_nss_init);
|
||||
module_exit(rmnet_nss_exit);
|
||||
27
wwan/driver/rmnet-nss/src/rmnet_nss.h
Normal file
27
wwan/driver/rmnet-nss/src/rmnet_nss.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef _RMNET_NSS_H_
|
||||
#define _RMNET_NSS_H_
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
struct rmnet_nss_cb {
|
||||
int (*nss_create)(struct net_device *dev);
|
||||
int (*nss_free)(struct net_device *dev);
|
||||
int (*nss_tx)(struct sk_buff *skb);
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user