Merge Mainline

This commit is contained in:
CN_SZTL 2020-08-23 01:30:00 +08:00
commit abc38b2132
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
31 changed files with 1122 additions and 58 deletions

View File

@ -61,7 +61,7 @@ endif
DEFAULT_PACKAGES += $(DEFAULT_PACKAGES.$(DEVICE_TYPE))
filter_packages = $(filter-out -% $(patsubst -%,%,$(filter -%,$(1))),$(1))
extra_packages = $(if $(filter wpad-mini wpad-basic wpad nas,$(1)),iwinfo)
extra_packages = $(if $(filter wpad-mini wpad-basic wpad-basic-wolfssl wpad nas,$(1)),iwinfo)
define ProfileDefault
NAME:=

View File

@ -10,9 +10,9 @@ function index()
end
local page
page = entry({"admin", "vpn", "gost"}, cbi("gost"), _("Gost"), 100)
page = entry({"admin", "services", "gost"}, cbi("gost"), _("Gost"), 100)
page.dependent = true
entry({"admin", "vpn", "gost", "status"},call("act_status")).leaf=true
entry({"admin", "services", "gost", "status"},call("act_status")).leaf=true
end
function act_status()

View File

@ -1,5 +1,5 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[vpn]], [[gost]], [[status]])%>', null,
XHR.poll(3, '<%=url([[admin]], [[services]], [[gost]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('gost_status');
if (data && tb) {

View File

@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openclash
PKG_VERSION:=0.39.7
PKG_VERSION:=0.39.8
PKG_RELEASE:=beta
PKG_MAINTAINER:=vernesong <https://github.com/vernesong/OpenClash>

File diff suppressed because one or more lines are too long

View File

@ -192,35 +192,29 @@
function core_update(btn)
{
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "coreupdate")%>', null, function(x, status) {
btn.value = '<%:检查并更新%>';
btn.disabled = false;
return false;
return false;
});
}
function core_tun_update(btn)
{
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "coretunupdate")%>', null, function(x, status) {
btn.value = '<%:检查并更新%>';
btn.disabled = false;
return false;
return false;
});
}
function core_game_update(btn)
{
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "coregameupdate")%>', null, function(x, status) {
btn.value = '<%:检查并更新%>';
btn.disabled = false;
return false;
return false;
});
}
function op_update(btn)

View File

@ -1 +1 @@
<!doctype html><html lang="en" dir="ltr"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel="icon" type="image/x-icon" href="https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png"/><title>Clash</title><link href="main.658aa6a6e3feec8f168b.css" rel="stylesheet"></head><body><div id="root"></div><script src="js/1.bundle.658aa6a6e3feec8f168b.min.js"></script><script src="js/bundle.658aa6a6e3feec8f168b.min.js"></script></body></html>
<!doctype html><html lang="en" dir="ltr"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel="icon" type="image/x-icon" href="https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png"/><title>Clash</title><link href="main.01d1a3c82895ade21dbf.css" rel="stylesheet"></head><body><div id="root"></div><script src="js/1.bundle.01d1a3c82895ade21dbf.min.js"></script><script src="js/bundle.01d1a3c82895ade21dbf.min.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,6 @@
#!/bin/bash
#!/bin/sh
. /lib/functions.sh
#禁止多个实例
status=$(ps|grep -c /usr/share/openclash/openclash_core.sh)
[ "$status" -gt "3" ] && exit 0
START_LOG="/tmp/openclash_start.log"
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
LOG_FILE="/tmp/openclash.log"
@ -50,11 +46,11 @@ if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
case $CORE_TYPE in
"Tun")
echo "正在下载【Tun】版本内核如下载失败请尝试手动下载并上传..." >$START_LOG
curl -sL -m 30 --retry 2 -x http://$PROXY_ADDR:$HTTP_PORT -U "$PROXY_AUTH" https://github.com/vernesong/OpenClash/releases/download/TUN-Premium/clash-"$CPU_MODEL"-"$CORE_LV".gz -o /tmp/clash.gz >/dev/null 2>&1
curl -sL -m 30 --retry 2 -x http://$PROXY_ADDR:$HTTP_PORT -U "$PROXY_AUTH" https://github.com/vernesong/OpenClash/releases/download/TUN-Premium/clash-"$CPU_MODEL"-"$CORE_LV".gz -o /tmp/clash_tun.gz >/dev/null 2>&1
;;
"Game")
echo "正在下载【Game】版本内核如下载失败请尝试手动下载并上传..." >$START_LOG
curl -sL -m 30 --retry 2 -x http://$PROXY_ADDR:$HTTP_PORT -U "$PROXY_AUTH" https://github.com/vernesong/OpenClash/releases/download/TUN/clash-"$CPU_MODEL".tar.gz -o /tmp/clash.tar.gz >/dev/null 2>&1
curl -sL -m 30 --retry 2 -x http://$PROXY_ADDR:$HTTP_PORT -U "$PROXY_AUTH" https://github.com/vernesong/OpenClash/releases/download/TUN/clash-"$CPU_MODEL".tar.gz -o /tmp/clash_game.tar.gz >/dev/null 2>&1
;;
*)
echo "正在下载【Dev】版本内核如下载失败请尝试手动下载并上传..." >$START_LOG
@ -64,11 +60,11 @@ if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
case $CORE_TYPE in
"Tun")
echo "正在下载【Tun】版本内核如下载失败请尝试手动下载并上传..." >$START_LOG
curl -sL -m 30 --retry 2 https://github.com/vernesong/OpenClash/releases/download/TUN-Premium/clash-"$CPU_MODEL"-"$CORE_LV".gz -o /tmp/clash.gz >/dev/null 2>&1
curl -sL -m 30 --retry 2 https://github.com/vernesong/OpenClash/releases/download/TUN-Premium/clash-"$CPU_MODEL"-"$CORE_LV".gz -o /tmp/clash_tun.gz >/dev/null 2>&1
;;
"Game")
echo "正在下载【Game】版本内核如下载失败请尝试手动下载并上传..." >$START_LOG
curl -sL -m 30 --retry 2 https://github.com/vernesong/OpenClash/releases/download/TUN/clash-"$CPU_MODEL".tar.gz -o /tmp/clash.tar.gz >/dev/null 2>&1
curl -sL -m 30 --retry 2 https://github.com/vernesong/OpenClash/releases/download/TUN/clash-"$CPU_MODEL".tar.gz -o /tmp/clash_game.tar.gz >/dev/null 2>&1
;;
*)
echo "正在下载【Dev】版本内核如下载失败请尝试手动下载并上传..." >$START_LOG
@ -78,27 +74,32 @@ if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
if [ "$?" -eq "0" ]; then
case $CORE_TYPE in
"Tun")
[ -s "/tmp/clash.gz" ] && {
gzip -d /tmp/clash.gz >/dev/null 2>&1
[ -s "/tmp/clash_tun.gz" ] && {
gzip -d /tmp/clash_tun.gz >/dev/null 2>&1 && mv /tmp/clash /tmp/clash_tun >/dev/null 2>&1
rm -rf /tmp/clash_tun.gz >/dev/null 2>&1
rm -rf /etc/openclash/core/clash_tun >/dev/null 2>&1
chmod 4755 /tmp/clash_tun >/dev/null 2>&1
chown root:root /tmp/clash_tun >/dev/null 2>&1
}
;;
"Game")
[ -s "/tmp/clash.tar.gz" ] && {
tar zxvf /tmp/clash.tar.gz -C /tmp >/dev/null 2>&1
[ -s "/tmp/clash_game.tar.gz" ] && {
tar zxvf /tmp/clash_game.tar.gz -C /tmp >/dev/null 2>&1 && mv /tmp/clash /tmp/clash_game >/dev/null 2>&1
rm -rf /tmp/clash_game.tar.gz >/dev/null 2>&1
rm -rf /etc/openclash/core/clash_game >/dev/null 2>&1
chmod 4755 /tmp/clash_game >/dev/null 2>&1
chown root:root /tmp/clash_game >/dev/null 2>&1
}
;;
*)
[ -s "/tmp/clash.tar.gz" ] && {
tar zxvf /tmp/clash.tar.gz -C /tmp >/dev/null 2>&1
tar zxvf /tmp/clash.tar.gz -C /tmp
rm -rf /tmp/clash.tar.gz >/dev/null 2>&1
rm -rf /etc/openclash/core/clash >/dev/null 2>&1
chmod 4755 /tmp/clash >/dev/null 2>&1
chown root:root /tmp/clash >/dev/null 2>&1
}
esac
chmod 4755 /tmp/clash >/dev/null 2>&1
chown root:root /tmp/clash >/dev/null 2>&1
rm -rf /tmp/clash.tar.gz >/dev/null 2>&1
rm -rf /tmp/clash.gz >/dev/null 2>&1
mkdir -p /etc/openclash/core
if [ "$if_restart" -eq 1 ]; then
kill -9 "$(pidof clash|sed 's/$//g')" 2>/dev/null
@ -107,31 +108,49 @@ if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
echo "【"$CORE_TYPE"】版本内核下载成功,开始更新..." >$START_LOG
case $CORE_TYPE in
"Tun")
mv /tmp/clash /etc/openclash/core/clash_tun >/dev/null 2>&1
mv /tmp/clash_tun /etc/openclash/core/clash_tun >/dev/null 2>&1
;;
"Game")
mv /tmp/clash /etc/openclash/core/clash_game >/dev/null 2>&1
mv /tmp/clash_game /etc/openclash/core/clash_game >/dev/null 2>&1
;;
*)
mv /tmp/clash /etc/openclash/core/clash >/dev/null 2>&1
esac
if [ "$?" -eq "0" ]; then
[ "$if_restart" -eq 1 ] && /etc/init.d/openclash start
echo "【"$CORE_TYPE"】版本内核更新成功!" >$START_LOG
echo "${LOGTIME} OpenClash 【"$CORE_TYPE"】 Core Update Successful" >>$LOG_FILE
sleep 5
sleep 3
[ "$if_restart" -eq 1 ] && /etc/init.d/openclash start
echo "" >$START_LOG
else
echo "【"$CORE_TYPE"】版本内核更新失败,请确认设备闪存空间足够后再试!" >$START_LOG
echo "${LOGTIME} OpenClash 【"$CORE_TYPE"】 Core Update Error" >>$LOG_FILE
rm -rf /tmp/clash >/dev/null 2>&1
case $CORE_TYPE in
"Tun")
rm -rf /tmp/clash_tun >/dev/null 2>&1
;;
"Game")
rm -rf /tmp/clash_game >/dev/null 2>&1
;;
*)
rm -rf /tmp/clash >/dev/null 2>&1
esac
sleep 5
echo "" >$START_LOG
fi
else
echo "【"$CORE_TYPE"】版本内核下载失败,请检查网络或稍后再试!" >$START_LOG
echo "${LOGTIME} OpenClash 【"$CORE_TYPE"】 Core Update Error" >>$LOG_FILE
rm -rf /tmp/clash >/dev/null 2>&1
case $CORE_TYPE in
"Tun")
rm -rf /tmp/clash_tun >/dev/null 2>&1
;;
"Game")
rm -rf /tmp/clash_game >/dev/null 2>&1
;;
*)
rm -rf /tmp/clash >/dev/null 2>&1
esac
sleep 10
echo "" >$START_LOG
fi

View File

@ -1,6 +1,6 @@
#!/bin/sh
uci set luci.main.lang=zh_cn
uci set luci.main.lang=auto
uci commit luci
uci set system.@system[0].timezone=CST-8

View File

@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-unblockmusic
PKG_VERSION:=2.3.5
PKG_RELEASE:=8
PKG_RELEASE:=9
PKG_CONFIG_DEPENDS := \
CONFIG_UnblockNeteaseMusic_Go \

View File

@ -51,6 +51,7 @@ flac.default = "1"
flac.rmempty = false
flac.description = translate("目前仅支持酷我、QQ、咪咕")
flac:depends("apptype", "nodejs")
flac:depends("apptype", "go")
force = s:option(Flag, "force_enabled", translate("强制替换为高音质歌曲"))
force.default = "1"

View File

@ -184,7 +184,10 @@ start()
add_cron
echo "$(date -R) # UnblockNeteaseMusic Nodejs Version (http:5200, https:5201)" >>/tmp/unblockmusic.log
elif [ "$APPTYPE" == "go" ]; then
UnblockNeteaseMusic -p 5200 -sp 5201 -m 0 -c /usr/share/UnblockNeteaseMusicGo/server.crt -k /usr/share/UnblockNeteaseMusicGo/server.key -m 0 -e >>/tmp/unblockmusic.log 2>&1 &
if [ $FLAC -eq 1 ]; then
ENABLE_FLAC="-b "
fi
UnblockNeteaseMusic $ENABLE_FLAC -p 5200 -sp 5201 -m 0 -c /usr/share/UnblockNeteaseMusicGo/server.crt -k /usr/share/UnblockNeteaseMusicGo/server.key -m 0 -e >>/tmp/unblockmusic.log 2>&1 &
echo "$(date -R) # UnblockNeteaseMusic Golang Version (http:5200, https:5201)" >>/tmp/unblockmusic.log
else
kill -9 $(busybox ps -w | grep 'sleep 60m' | grep -v grep | awk '{print $1}') >/dev/null 2>&1

View File

@ -5,7 +5,7 @@ LUCI_TITLE:=LuCI support for UnblockNeteaseMusic Go
LUCI_DEPENDS:=+bash +busybox +coreutils-nohup +curl +dnsmasq-full +openssl-util +ipset +UnblockNeteaseMusic-Go
LUCI_PKGARCH:=all
PKG_NAME:=luci-app-unblockneteasemusic-go
PKG_VERSION:=1.6
PKG_VERSION:=1.8
PKG_RELEASE:=1
PKG_MAINTAINER:=https://github.com/cnsilvan/luci-app-unblockneteasemusic

View File

@ -1,7 +1,7 @@
local http = luci.http
mp = Map("unblockneteasemusic", translate("解除网易云音乐播放限制 (Golang)"))
mp.description = translate("原理:采用 [酷我/酷狗/咕咪] 音源(后续有空补充),替换网易云音乐 灰色 歌曲链接<br/>具体使用方法参见https://github.com/cnsilvan/luci-app-unblockneteasemusic<br/>首次使用会自动生成证书,可能较慢")
mp.description = translate("原理:采用 [酷我/酷狗/咕咪] 音源(后续有空补充),替换网易云音乐 灰色 歌曲链接<br/>具体使用方法参见https://github.com/cnsilvan/luci-app-unblockneteasemusic<br/>提示客户端网易云音乐能用就别升级app最新版本不一定能用")
mp:section(SimpleSection).template = "unblockneteasemusic/unblockneteasemusic_status"
s = mp:section(TypedSection, "unblockneteasemusic")
@ -48,16 +48,22 @@ hijack.description = translate("如果使用Hosts劫持请将HTTP/HTTPS端口
hijack.default = "dont_hijack"
hijack.rmempty = false
daemon_enable = s:option(Flag, "daemon_enable", translate("启用进程守护"))
daemon_enable.description = translate("开启后,附属程序会自动检测主程序运行状态,在主程序退出时自动重启")
daemon_enable.default = 0
daemon_enable.rmempty = false
endpoint_enable = s:option(Flag, "endpoint_enable", translate("启用地址转换"))
endpoint_enable.description = translate("开启后设备需要信任证书经测试ios设备需要开启其他设备无法使用时再开启尝试")
endpoint_enable.description = translate("开启后设备需要信任证书经测试ios设备需要开启android设备使用咪咕源下载时需要开启其他情况无法使用时再开启尝试")
endpoint_enable.default = 0
endpoint_enable.rmempty = false
force_best_quality = s:option(Flag, "force_best_quality", translate("强制音质优先"))
force_best_quality.description = translate("开启后,客户端选择音质将失效")
force_best_quality.default = 0
force_best_quality.rmempty = false
delete = s:option(Button,"_delete", translate("删除根证书"))
delete.description = translate("删除证书,以便下次启动时生成,可用于解决过期证书等问题")
delete.inputstyle = "reload"
@ -65,7 +71,7 @@ delete.write = function()
delete_()
end
download = s:option(Button,"_download", translate("下载根证书"))
download.description = translate("请在客户端信任该证书。该证书由你设备自动生成,安全可靠")
download.description = translate("请在客户端信任该证书。该证书由你设备自动生成,安全可靠<br/>IOS信任证书步骤1. 安装证书--设置-描述文件-安装 2. 通用-关于本机-证书信任设置-启动完全信任")
download.inputstyle = "reload"
download.write = function()
download_()

View File

@ -5,7 +5,7 @@ f = SimpleForm("logview")
t = f:field(TextValue, "conf")
t.rmempty = true
t.rows = 15
t.rows = 20
function t.cfgvalue()
return fs.readfile(conffile) or ""
end

View File

@ -1,6 +1,6 @@
#!/bin/sh /etc/rc.common
START=80
START=96
STOP=10
lan_addr="$(uci get network.lan.ipaddr)"
@ -12,6 +12,7 @@ music_source="$(uci get unblockneteasemusic.@unblockneteasemusic[0].music_source
music_customize_source="$(uci get unblockneteasemusic.@unblockneteasemusic[0].music_customize_source)"
hijack_ways="$(uci get unblockneteasemusic.@unblockneteasemusic[0].hijack_ways)"
endpoint="$(uci get unblockneteasemusic.@unblockneteasemusic[0].endpoint_enable)"
force_best_quality="$(uci get unblockneteasemusic.@unblockneteasemusic[0].force_best_quality)"
extFile="/usr/share/UnblockNeteaseMusic/extFile.txt"
serverCrt="/usr/share/UnblockNeteaseMusic/server.crt"
serverKey="/usr/share/UnblockNeteaseMusic/server.key"
@ -135,6 +136,9 @@ start() {
if [ "${endpoint}" = "1" ]; then
exArgs="-e"
fi
if [ "${force_best_quality}" = "1" ]; then
exArgs="${exArgs} -b"
fi
if [ "${music_source}" = "default" ]; then
nohup UnblockNeteaseMusic -p "${http_port}" -sp "${https_port}" -o kuwo -m 0 -c "${serverCrt}" -k "${serverKey}" ${exArgs} >>"${logFile}" 2>&1 &
else

View File

@ -35,7 +35,7 @@ ifeq ($(ARCH),aarch64)
endif
PKG_NAME:=brook
PKG_VERSION:=20200801
PKG_VERSION:=20200901
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=brook$(PKG_ARCH_BROOK)

View File

@ -13,6 +13,7 @@ config WPA_RFKILL_SUPPORT
PACKAGE_wpad-openssl || \
PACKAGE_wpad-wolfssl || \
PACKAGE_wpad-basic || \
PACKAGE_wpad-basic-wolfssl || \
PACKAGE_wpad-mini || \
PACKAGE_wpad-mesh-openssl || \
PACKAGE_wpad-mesh-wolfssl
@ -32,6 +33,7 @@ config WPA_MSG_MIN_PRIORITY
PACKAGE_wpad-openssl || \
PACKAGE_wpad-wolfssl || \
PACKAGE_wpad-basic || \
PACKAGE_wpad-basic-wolfssl || \
PACKAGE_wpad-mini || \
PACKAGE_wpad-mesh-openssl || \
PACKAGE_wpad-mesh-wolfssl
@ -49,6 +51,7 @@ config WPA_WOLFSSL
bool
default PACKAGE_wpa-supplicant-wolfssl ||\
PACKAGE_wpad-wolfssl ||\
PACKAGE_wpad-basic-wolfssl || \
PACKAGE_wpad-mesh-wolfssl ||\
PACKAGE_eapol-test-wolfssl
select WOLFSSL_HAS_AES_CCM

View File

@ -241,6 +241,17 @@ define Package/wpad-basic/description
This package contains a basic IEEE 802.1x/WPA Authenticator and Supplicant with WPA-PSK, 802.11r and 802.11w support.
endef
define Package/wpad-basic-wolfssl
$(call Package/wpad/Default,$(1))
TITLE+= (wolfSSL, 11r, 11w)
VARIANT:=wpad-basic-wolfssl
DEPENDS+=+libwolfssl
endef
define Package/wpad-basic-wolfssl/description
This package contains a basic IEEE 802.1x/WPA Authenticator and Supplicant with WPA-PSK, SAE (WPA3-Personal), 802.11r and 802.11w support.
endef
define Package/wpad-mini
$(call Package/wpad/Default,$(1))
TITLE+= (WPA-PSK only)
@ -556,6 +567,7 @@ define Package/wpad/install
$(LN) wpad $(1)/usr/sbin/wpa_supplicant
endef
Package/wpad-basic/install = $(Package/wpad/install)
Package/wpad-basic-wolfssl/install = $(Package/wpad/install)
Package/wpad-mini/install = $(Package/wpad/install)
Package/wpad-openssl/install = $(Package/wpad/install)
Package/wpad-wolfssl/install = $(Package/wpad/install)
@ -611,6 +623,7 @@ $(eval $(call BuildPackage,wpad))
$(eval $(call BuildPackage,wpad-mesh-openssl))
$(eval $(call BuildPackage,wpad-mesh-wolfssl))
$(eval $(call BuildPackage,wpad-basic))
$(eval $(call BuildPackage,wpad-basic-wolfssl))
$(eval $(call BuildPackage,wpad-mini))
$(eval $(call BuildPackage,wpad-openssl))
$(eval $(call BuildPackage,wpad-wolfssl))

View File

@ -8,7 +8,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=curl
PKG_VERSION:=7.71.1
PKG_VERSION:=7.72.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
@ -16,7 +16,7 @@ PKG_SOURCE_URL:=https://dl.uxnr.de/mirror/curl/ \
https://curl.mirror.anstey.ca/ \
https://curl.askapache.com/download/ \
https://curl.haxx.se/download/
PKG_HASH:=40f83eda27cdbeb25cd4da48cefb639af1b9395d6026d2da1825bf059239658c
PKG_HASH:=0ded0808c4d85f2ee0db86980ae610cc9d165e9ca9da466196cc73c346513713
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=COPYING

View File

@ -16,12 +16,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=smartdns
PKG_VERSION:=8.2020.16
PKG_VERSION:=8.2020.22
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/pymumu/smartdns.git
PKG_SOURCE_VERSION:=69ba3f8789b65681ab0a8d396e216e0efb186ec2
PKG_SOURCE_VERSION:=80cb27c795572552ea561f2334a9d12602518d3e
PKG_MAINTAINER:=Nick Peng <pymumu@gmail.com>
PKG_LICENSE:=GPL-3.0-or-later

View File

@ -0,0 +1,339 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Sun, 26 Jul 2020 14:03:21 +0200
Subject: [PATCH] net: add support for threaded NAPI polling
For some drivers (especially 802.11 drivers), doing a lot of work in the NAPI
poll function does not perform well. Since NAPI poll is bound to the CPU it
was scheduled from, we can easily end up with a few very busy CPUs spending
most of their time in softirq/ksoftirqd and some idle ones.
Introduce threaded NAPI for such drivers based on a workqueue. The API is the
same except for using netif_threaded_napi_add instead of netif_napi_add.
In my tests with mt76 on MT7621 using threaded NAPI + a thread for tx scheduling
improves LAN->WLAN bridging throughput by 10-50%. Throughput without threaded
NAPI is wildly inconsistent, depending on the CPU that runs the tx scheduling
thread.
With threaded NAPI it seems stable and consistent (and higher than the best
results I got without it).
Based on a patch by Hillf Danton
Cc: Hillf Danton <hdanton@sina.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -326,6 +326,7 @@ struct napi_struct {
struct list_head dev_list;
struct hlist_node napi_hash_node;
unsigned int napi_id;
+ struct work_struct work;
};
enum {
@@ -336,6 +337,7 @@ enum {
NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */
NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */
+ NAPI_STATE_THREADED, /* Use threaded NAPI */
};
enum {
@@ -346,6 +348,7 @@ enum {
NAPIF_STATE_HASHED = BIT(NAPI_STATE_HASHED),
NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL),
NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL),
+ NAPIF_STATE_THREADED = BIT(NAPI_STATE_THREADED),
};
enum gro_result {
@@ -2093,6 +2096,26 @@ void netif_napi_add(struct net_device *d
int (*poll)(struct napi_struct *, int), int weight);
/**
+ * netif_threaded_napi_add - initialize a NAPI context
+ * @dev: network device
+ * @napi: NAPI context
+ * @poll: polling function
+ * @weight: default weight
+ *
+ * This variant of netif_napi_add() should be used from drivers using NAPI
+ * with CPU intensive poll functions.
+ * This will schedule polling from a high priority workqueue
+ */
+static inline void netif_threaded_napi_add(struct net_device *dev,
+ struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int),
+ int weight)
+{
+ set_bit(NAPI_STATE_THREADED, &napi->state);
+ netif_napi_add(dev, napi, poll, weight);
+}
+
+/**
* netif_tx_napi_add - initialize a NAPI context
* @dev: network device
* @napi: NAPI context
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -160,6 +160,7 @@ static DEFINE_SPINLOCK(offload_lock);
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly; /* Taps */
static struct list_head offload_base __read_mostly;
+static struct workqueue_struct *napi_workq __read_mostly;
static int netif_rx_internal(struct sk_buff *skb);
static int call_netdevice_notifiers_info(unsigned long val,
@@ -5237,6 +5238,11 @@ void __napi_schedule(struct napi_struct
{
unsigned long flags;
+ if (test_bit(NAPI_STATE_THREADED, &n->state)) {
+ queue_work(napi_workq, &n->work);
+ return;
+ }
+
local_irq_save(flags);
____napi_schedule(this_cpu_ptr(&softnet_data), n);
local_irq_restore(flags);
@@ -5284,6 +5290,11 @@ EXPORT_SYMBOL(napi_schedule_prep);
*/
void __napi_schedule_irqoff(struct napi_struct *n)
{
+ if (test_bit(NAPI_STATE_THREADED, &n->state)) {
+ queue_work(napi_workq, &n->work);
+ return;
+ }
+
____napi_schedule(this_cpu_ptr(&softnet_data), n);
}
EXPORT_SYMBOL(__napi_schedule_irqoff);
@@ -5521,6 +5532,82 @@ static enum hrtimer_restart napi_watchdo
return HRTIMER_NORESTART;
}
+static int __napi_poll(struct napi_struct *n, bool *repoll)
+{
+ int work, weight;
+
+ weight = n->weight;
+
+ /* This NAPI_STATE_SCHED test is for avoiding a race
+ * with netpoll's poll_napi(). Only the entity which
+ * obtains the lock and sees NAPI_STATE_SCHED set will
+ * actually make the ->poll() call. Therefore we avoid
+ * accidentally calling ->poll() when NAPI is not scheduled.
+ */
+ work = 0;
+ if (test_bit(NAPI_STATE_SCHED, &n->state)) {
+ work = n->poll(n, weight);
+ trace_napi_poll(n, work, weight);
+ }
+
+ WARN_ON_ONCE(work > weight);
+
+ if (likely(work < weight))
+ return work;
+
+ /* Drivers must not modify the NAPI state if they
+ * consume the entire weight. In such cases this code
+ * still "owns" the NAPI instance and therefore can
+ * move the instance around on the list at-will.
+ */
+ if (unlikely(napi_disable_pending(n))) {
+ napi_complete(n);
+ return work;
+ }
+
+ if (n->gro_list) {
+ /* flush too old packets
+ * If HZ < 1000, flush all packets.
+ */
+ napi_gro_flush(n, HZ >= 1000);
+ }
+
+ *repoll = true;
+
+ return work;
+}
+
+static void napi_workfn(struct work_struct *work)
+{
+ struct napi_struct *n = container_of(work, struct napi_struct, work);
+ void *have;
+
+ for (;;) {
+ bool repoll = false;
+
+ local_bh_disable();
+
+ have = netpoll_poll_lock(n);
+ __napi_poll(n, &repoll);
+ netpoll_poll_unlock(have);
+
+ local_bh_enable();
+
+ if (!repoll)
+ return;
+
+ if (!need_resched())
+ continue;
+
+ /*
+ * have to pay for the latency of task switch even if
+ * napi is scheduled
+ */
+ queue_work(napi_workq, work);
+ return;
+ }
+}
+
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight)
{
@@ -5540,6 +5627,7 @@ void netif_napi_add(struct net_device *d
#ifdef CONFIG_NETPOLL
napi->poll_owner = -1;
#endif
+ INIT_WORK(&napi->work, napi_workfn);
set_bit(NAPI_STATE_SCHED, &napi->state);
napi_hash_add(napi);
}
@@ -5565,6 +5653,7 @@ EXPORT_SYMBOL(napi_disable);
void netif_napi_del(struct napi_struct *napi)
{
might_sleep();
+ cancel_work_sync(&napi->work);
if (napi_hash_del(napi))
synchronize_net();
list_del_init(&napi->dev_list);
@@ -5578,48 +5667,18 @@ EXPORT_SYMBOL(netif_napi_del);
static int napi_poll(struct napi_struct *n, struct list_head *repoll)
{
+ bool do_repoll = false;
void *have;
- int work, weight;
+ int work;
list_del_init(&n->poll_list);
have = netpoll_poll_lock(n);
- weight = n->weight;
-
- /* This NAPI_STATE_SCHED test is for avoiding a race
- * with netpoll's poll_napi(). Only the entity which
- * obtains the lock and sees NAPI_STATE_SCHED set will
- * actually make the ->poll() call. Therefore we avoid
- * accidentally calling ->poll() when NAPI is not scheduled.
- */
- work = 0;
- if (test_bit(NAPI_STATE_SCHED, &n->state)) {
- work = n->poll(n, weight);
- trace_napi_poll(n, work, weight);
- }
-
- WARN_ON_ONCE(work > weight);
-
- if (likely(work < weight))
- goto out_unlock;
+ work = __napi_poll(n, &do_repoll);
- /* Drivers must not modify the NAPI state if they
- * consume the entire weight. In such cases this code
- * still "owns" the NAPI instance and therefore can
- * move the instance around on the list at-will.
- */
- if (unlikely(napi_disable_pending(n))) {
- napi_complete(n);
+ if (!do_repoll)
goto out_unlock;
- }
-
- if (n->gro_list) {
- /* flush too old packets
- * If HZ < 1000, flush all packets.
- */
- napi_gro_flush(n, HZ >= 1000);
- }
/* Some drivers may have called napi_schedule
* prior to exhausting their budget.
@@ -8855,6 +8914,10 @@ static int __init net_dev_init(void)
sd->backlog.weight = weight_p;
}
+ napi_workq = alloc_workqueue("napi_workq", WQ_UNBOUND | WQ_HIGHPRI,
+ WQ_UNBOUND_MAX_ACTIVE | WQ_SYSFS);
+ BUG_ON(!napi_workq);
+
dev_boot_phase = 0;
/* The loopback device is special if any other network devices
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -441,6 +441,52 @@ static ssize_t proto_down_store(struct d
}
NETDEVICE_SHOW_RW(proto_down, fmt_dec);
+static int change_napi_threaded(struct net_device *dev, unsigned long val)
+{
+ struct napi_struct *napi;
+
+ if (list_empty(&dev->napi_list))
+ return -EOPNOTSUPP;
+
+ list_for_each_entry(napi, &dev->napi_list, dev_list) {
+ if (val)
+ set_bit(NAPI_STATE_THREADED, &napi->state);
+ else
+ clear_bit(NAPI_STATE_THREADED, &napi->state);
+ }
+
+ return 0;
+}
+
+static ssize_t napi_threaded_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return netdev_store(dev, attr, buf, len, change_napi_threaded);
+}
+
+static ssize_t napi_threaded_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ struct napi_struct *napi;
+ bool enabled = false;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ list_for_each_entry(napi, &netdev->napi_list, dev_list) {
+ if (test_bit(NAPI_STATE_THREADED, &napi->state))
+ enabled = true;
+ }
+
+ rtnl_unlock();
+
+ return sprintf(buf, fmt_dec, enabled);
+}
+static DEVICE_ATTR_RW(napi_threaded);
+
static ssize_t phys_port_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -536,6 +582,7 @@ static struct attribute *net_class_attrs
&dev_attr_flags.attr,
&dev_attr_tx_queue_len.attr,
&dev_attr_gro_flush_timeout.attr,
+ &dev_attr_napi_threaded.attr,
&dev_attr_phys_port_id.attr,
&dev_attr_phys_port_name.attr,
&dev_attr_phys_switch_id.attr,

View File

@ -0,0 +1,339 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Sun, 26 Jul 2020 14:03:21 +0200
Subject: [PATCH] net: add support for threaded NAPI polling
For some drivers (especially 802.11 drivers), doing a lot of work in the NAPI
poll function does not perform well. Since NAPI poll is bound to the CPU it
was scheduled from, we can easily end up with a few very busy CPUs spending
most of their time in softirq/ksoftirqd and some idle ones.
Introduce threaded NAPI for such drivers based on a workqueue. The API is the
same except for using netif_threaded_napi_add instead of netif_napi_add.
In my tests with mt76 on MT7621 using threaded NAPI + a thread for tx scheduling
improves LAN->WLAN bridging throughput by 10-50%. Throughput without threaded
NAPI is wildly inconsistent, depending on the CPU that runs the tx scheduling
thread.
With threaded NAPI it seems stable and consistent (and higher than the best
results I got without it).
Based on a patch by Hillf Danton
Cc: Hillf Danton <hdanton@sina.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -339,6 +339,7 @@ struct napi_struct {
struct list_head dev_list;
struct hlist_node napi_hash_node;
unsigned int napi_id;
+ struct work_struct work;
};
enum {
@@ -349,6 +350,7 @@ enum {
NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */
NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */
+ NAPI_STATE_THREADED, /* Use threaded NAPI */
};
enum {
@@ -359,6 +361,7 @@ enum {
NAPIF_STATE_HASHED = BIT(NAPI_STATE_HASHED),
NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL),
NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL),
+ NAPIF_STATE_THREADED = BIT(NAPI_STATE_THREADED),
};
enum gro_result {
@@ -2230,6 +2233,26 @@ void netif_napi_add(struct net_device *d
int (*poll)(struct napi_struct *, int), int weight);
/**
+ * netif_threaded_napi_add - initialize a NAPI context
+ * @dev: network device
+ * @napi: NAPI context
+ * @poll: polling function
+ * @weight: default weight
+ *
+ * This variant of netif_napi_add() should be used from drivers using NAPI
+ * with CPU intensive poll functions.
+ * This will schedule polling from a high priority workqueue
+ */
+static inline void netif_threaded_napi_add(struct net_device *dev,
+ struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int),
+ int weight)
+{
+ set_bit(NAPI_STATE_THREADED, &napi->state);
+ netif_napi_add(dev, napi, poll, weight);
+}
+
+/**
* netif_tx_napi_add - initialize a NAPI context
* @dev: network device
* @napi: NAPI context
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -160,6 +160,7 @@ static DEFINE_SPINLOCK(offload_lock);
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly; /* Taps */
static struct list_head offload_base __read_mostly;
+static struct workqueue_struct *napi_workq __read_mostly;
static int netif_rx_internal(struct sk_buff *skb);
static int call_netdevice_notifiers_info(unsigned long val,
@@ -5891,6 +5892,11 @@ void __napi_schedule(struct napi_struct
{
unsigned long flags;
+ if (test_bit(NAPI_STATE_THREADED, &n->state)) {
+ queue_work(napi_workq, &n->work);
+ return;
+ }
+
local_irq_save(flags);
____napi_schedule(this_cpu_ptr(&softnet_data), n);
local_irq_restore(flags);
@@ -5938,6 +5944,11 @@ EXPORT_SYMBOL(napi_schedule_prep);
*/
void __napi_schedule_irqoff(struct napi_struct *n)
{
+ if (test_bit(NAPI_STATE_THREADED, &n->state)) {
+ queue_work(napi_workq, &n->work);
+ return;
+ }
+
____napi_schedule(this_cpu_ptr(&softnet_data), n);
}
EXPORT_SYMBOL(__napi_schedule_irqoff);
@@ -6186,6 +6197,82 @@ static void init_gro_hash(struct napi_st
napi->gro_bitmask = 0;
}
+static int __napi_poll(struct napi_struct *n, bool *repoll)
+{
+ int work, weight;
+
+ weight = n->weight;
+
+ /* This NAPI_STATE_SCHED test is for avoiding a race
+ * with netpoll's poll_napi(). Only the entity which
+ * obtains the lock and sees NAPI_STATE_SCHED set will
+ * actually make the ->poll() call. Therefore we avoid
+ * accidentally calling ->poll() when NAPI is not scheduled.
+ */
+ work = 0;
+ if (test_bit(NAPI_STATE_SCHED, &n->state)) {
+ work = n->poll(n, weight);
+ trace_napi_poll(n, work, weight);
+ }
+
+ WARN_ON_ONCE(work > weight);
+
+ if (likely(work < weight))
+ return work;
+
+ /* Drivers must not modify the NAPI state if they
+ * consume the entire weight. In such cases this code
+ * still "owns" the NAPI instance and therefore can
+ * move the instance around on the list at-will.
+ */
+ if (unlikely(napi_disable_pending(n))) {
+ napi_complete(n);
+ return work;
+ }
+
+ if (n->gro_bitmask) {
+ /* flush too old packets
+ * If HZ < 1000, flush all packets.
+ */
+ napi_gro_flush(n, HZ >= 1000);
+ }
+
+ *repoll = true;
+
+ return work;
+}
+
+static void napi_workfn(struct work_struct *work)
+{
+ struct napi_struct *n = container_of(work, struct napi_struct, work);
+ void *have;
+
+ for (;;) {
+ bool repoll = false;
+
+ local_bh_disable();
+
+ have = netpoll_poll_lock(n);
+ __napi_poll(n, &repoll);
+ netpoll_poll_unlock(have);
+
+ local_bh_enable();
+
+ if (!repoll)
+ return;
+
+ if (!need_resched())
+ continue;
+
+ /*
+ * have to pay for the latency of task switch even if
+ * napi is scheduled
+ */
+ queue_work(napi_workq, work);
+ return;
+ }
+}
+
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight)
{
@@ -6204,6 +6291,7 @@ void netif_napi_add(struct net_device *d
#ifdef CONFIG_NETPOLL
napi->poll_owner = -1;
#endif
+ INIT_WORK(&napi->work, napi_workfn);
set_bit(NAPI_STATE_SCHED, &napi->state);
napi_hash_add(napi);
}
@@ -6242,6 +6330,7 @@ static void flush_gro_hash(struct napi_s
void netif_napi_del(struct napi_struct *napi)
{
might_sleep();
+ cancel_work_sync(&napi->work);
if (napi_hash_del(napi))
synchronize_net();
list_del_init(&napi->dev_list);
@@ -6254,48 +6343,18 @@ EXPORT_SYMBOL(netif_napi_del);
static int napi_poll(struct napi_struct *n, struct list_head *repoll)
{
+ bool do_repoll = false;
void *have;
- int work, weight;
+ int work;
list_del_init(&n->poll_list);
have = netpoll_poll_lock(n);
- weight = n->weight;
-
- /* This NAPI_STATE_SCHED test is for avoiding a race
- * with netpoll's poll_napi(). Only the entity which
- * obtains the lock and sees NAPI_STATE_SCHED set will
- * actually make the ->poll() call. Therefore we avoid
- * accidentally calling ->poll() when NAPI is not scheduled.
- */
- work = 0;
- if (test_bit(NAPI_STATE_SCHED, &n->state)) {
- work = n->poll(n, weight);
- trace_napi_poll(n, work, weight);
- }
-
- WARN_ON_ONCE(work > weight);
-
- if (likely(work < weight))
- goto out_unlock;
+ work = __napi_poll(n, &do_repoll);
- /* Drivers must not modify the NAPI state if they
- * consume the entire weight. In such cases this code
- * still "owns" the NAPI instance and therefore can
- * move the instance around on the list at-will.
- */
- if (unlikely(napi_disable_pending(n))) {
- napi_complete(n);
+ if (!do_repoll)
goto out_unlock;
- }
-
- if (n->gro_bitmask) {
- /* flush too old packets
- * If HZ < 1000, flush all packets.
- */
- napi_gro_flush(n, HZ >= 1000);
- }
/* Some drivers may have called napi_schedule
* prior to exhausting their budget.
@@ -9895,6 +9954,10 @@ static int __init net_dev_init(void)
sd->backlog.weight = weight_p;
}
+ napi_workq = alloc_workqueue("napi_workq", WQ_UNBOUND | WQ_HIGHPRI,
+ WQ_UNBOUND_MAX_ACTIVE | WQ_SYSFS);
+ BUG_ON(!napi_workq);
+
dev_boot_phase = 0;
/* The loopback device is special if any other network devices
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -447,6 +447,52 @@ static ssize_t proto_down_store(struct d
}
NETDEVICE_SHOW_RW(proto_down, fmt_dec);
+static int change_napi_threaded(struct net_device *dev, unsigned long val)
+{
+ struct napi_struct *napi;
+
+ if (list_empty(&dev->napi_list))
+ return -EOPNOTSUPP;
+
+ list_for_each_entry(napi, &dev->napi_list, dev_list) {
+ if (val)
+ set_bit(NAPI_STATE_THREADED, &napi->state);
+ else
+ clear_bit(NAPI_STATE_THREADED, &napi->state);
+ }
+
+ return 0;
+}
+
+static ssize_t napi_threaded_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return netdev_store(dev, attr, buf, len, change_napi_threaded);
+}
+
+static ssize_t napi_threaded_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ struct napi_struct *napi;
+ bool enabled = false;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ list_for_each_entry(napi, &netdev->napi_list, dev_list) {
+ if (test_bit(NAPI_STATE_THREADED, &napi->state))
+ enabled = true;
+ }
+
+ rtnl_unlock();
+
+ return sprintf(buf, fmt_dec, enabled);
+}
+static DEVICE_ATTR_RW(napi_threaded);
+
static ssize_t phys_port_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -542,6 +588,7 @@ static struct attribute *net_class_attrs
&dev_attr_flags.attr,
&dev_attr_tx_queue_len.attr,
&dev_attr_gro_flush_timeout.attr,
+ &dev_attr_napi_threaded.attr,
&dev_attr_phys_port_id.attr,
&dev_attr_phys_port_name.attr,
&dev_attr_phys_switch_id.attr,

View File

@ -0,0 +1,343 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Sun, 26 Jul 2020 14:03:21 +0200
Subject: [PATCH] net: add support for threaded NAPI polling
For some drivers (especially 802.11 drivers), doing a lot of work in the NAPI
poll function does not perform well. Since NAPI poll is bound to the CPU it
was scheduled from, we can easily end up with a few very busy CPUs spending
most of their time in softirq/ksoftirqd and some idle ones.
Introduce threaded NAPI for such drivers based on a workqueue. The API is the
same except for using netif_threaded_napi_add instead of netif_napi_add.
In my tests with mt76 on MT7621 using threaded NAPI + a thread for tx scheduling
improves LAN->WLAN bridging throughput by 10-50%. Throughput without threaded
NAPI is wildly inconsistent, depending on the CPU that runs the tx scheduling
thread.
With threaded NAPI it seems stable and consistent (and higher than the best
results I got without it).
Based on a patch by Hillf Danton
Cc: Hillf Danton <hdanton@sina.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -340,6 +340,7 @@ struct napi_struct {
struct list_head dev_list;
struct hlist_node napi_hash_node;
unsigned int napi_id;
+ struct work_struct work;
};
enum {
@@ -350,6 +351,7 @@ enum {
NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */
NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */
+ NAPI_STATE_THREADED, /* Use threaded NAPI */
};
enum {
@@ -360,6 +362,7 @@ enum {
NAPIF_STATE_HASHED = BIT(NAPI_STATE_HASHED),
NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL),
NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL),
+ NAPIF_STATE_THREADED = BIT(NAPI_STATE_THREADED),
};
enum gro_result {
@@ -2249,6 +2252,26 @@ void netif_napi_add(struct net_device *d
int (*poll)(struct napi_struct *, int), int weight);
/**
+ * netif_threaded_napi_add - initialize a NAPI context
+ * @dev: network device
+ * @napi: NAPI context
+ * @poll: polling function
+ * @weight: default weight
+ *
+ * This variant of netif_napi_add() should be used from drivers using NAPI
+ * with CPU intensive poll functions.
+ * This will schedule polling from a high priority workqueue
+ */
+static inline void netif_threaded_napi_add(struct net_device *dev,
+ struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int),
+ int weight)
+{
+ set_bit(NAPI_STATE_THREADED, &napi->state);
+ netif_napi_add(dev, napi, poll, weight);
+}
+
+/**
* netif_tx_napi_add - initialize a NAPI context
* @dev: network device
* @napi: NAPI context
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -156,6 +156,7 @@ static DEFINE_SPINLOCK(offload_lock);
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly; /* Taps */
static struct list_head offload_base __read_mostly;
+static struct workqueue_struct *napi_workq __read_mostly;
static int netif_rx_internal(struct sk_buff *skb);
static int call_netdevice_notifiers_info(unsigned long val,
@@ -5910,6 +5911,11 @@ void __napi_schedule(struct napi_struct
{
unsigned long flags;
+ if (test_bit(NAPI_STATE_THREADED, &n->state)) {
+ queue_work(napi_workq, &n->work);
+ return;
+ }
+
local_irq_save(flags);
____napi_schedule(this_cpu_ptr(&softnet_data), n);
local_irq_restore(flags);
@@ -5957,6 +5963,11 @@ EXPORT_SYMBOL(napi_schedule_prep);
*/
void __napi_schedule_irqoff(struct napi_struct *n)
{
+ if (test_bit(NAPI_STATE_THREADED, &n->state)) {
+ queue_work(napi_workq, &n->work);
+ return;
+ }
+
____napi_schedule(this_cpu_ptr(&softnet_data), n);
}
EXPORT_SYMBOL(__napi_schedule_irqoff);
@@ -6218,6 +6229,84 @@ static void init_gro_hash(struct napi_st
napi->gro_bitmask = 0;
}
+static int __napi_poll(struct napi_struct *n, bool *repoll)
+{
+ int work, weight;
+
+ weight = n->weight;
+
+ /* This NAPI_STATE_SCHED test is for avoiding a race
+ * with netpoll's poll_napi(). Only the entity which
+ * obtains the lock and sees NAPI_STATE_SCHED set will
+ * actually make the ->poll() call. Therefore we avoid
+ * accidentally calling ->poll() when NAPI is not scheduled.
+ */
+ work = 0;
+ if (test_bit(NAPI_STATE_SCHED, &n->state)) {
+ work = n->poll(n, weight);
+ trace_napi_poll(n, work, weight);
+ }
+
+ WARN_ON_ONCE(work > weight);
+
+ if (likely(work < weight))
+ return work;
+
+ /* Drivers must not modify the NAPI state if they
+ * consume the entire weight. In such cases this code
+ * still "owns" the NAPI instance and therefore can
+ * move the instance around on the list at-will.
+ */
+ if (unlikely(napi_disable_pending(n))) {
+ napi_complete(n);
+ return work;
+ }
+
+ if (n->gro_bitmask) {
+ /* flush too old packets
+ * If HZ < 1000, flush all packets.
+ */
+ napi_gro_flush(n, HZ >= 1000);
+ }
+
+ gro_normal_list(n);
+
+ *repoll = true;
+
+ return work;
+}
+
+static void napi_workfn(struct work_struct *work)
+{
+ struct napi_struct *n = container_of(work, struct napi_struct, work);
+ void *have;
+
+ for (;;) {
+ bool repoll = false;
+
+ local_bh_disable();
+
+ have = netpoll_poll_lock(n);
+ __napi_poll(n, &repoll);
+ netpoll_poll_unlock(have);
+
+ local_bh_enable();
+
+ if (!repoll)
+ return;
+
+ if (!need_resched())
+ continue;
+
+ /*
+ * have to pay for the latency of task switch even if
+ * napi is scheduled
+ */
+ queue_work(napi_workq, work);
+ return;
+ }
+}
+
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight)
{
@@ -6238,6 +6327,7 @@ void netif_napi_add(struct net_device *d
#ifdef CONFIG_NETPOLL
napi->poll_owner = -1;
#endif
+ INIT_WORK(&napi->work, napi_workfn);
set_bit(NAPI_STATE_SCHED, &napi->state);
napi_hash_add(napi);
}
@@ -6276,6 +6366,7 @@ static void flush_gro_hash(struct napi_s
void netif_napi_del(struct napi_struct *napi)
{
might_sleep();
+ cancel_work_sync(&napi->work);
if (napi_hash_del(napi))
synchronize_net();
list_del_init(&napi->dev_list);
@@ -6288,50 +6379,18 @@ EXPORT_SYMBOL(netif_napi_del);
static int napi_poll(struct napi_struct *n, struct list_head *repoll)
{
+ bool do_repoll = false;
void *have;
- int work, weight;
+ int work;
list_del_init(&n->poll_list);
have = netpoll_poll_lock(n);
- weight = n->weight;
-
- /* This NAPI_STATE_SCHED test is for avoiding a race
- * with netpoll's poll_napi(). Only the entity which
- * obtains the lock and sees NAPI_STATE_SCHED set will
- * actually make the ->poll() call. Therefore we avoid
- * accidentally calling ->poll() when NAPI is not scheduled.
- */
- work = 0;
- if (test_bit(NAPI_STATE_SCHED, &n->state)) {
- work = n->poll(n, weight);
- trace_napi_poll(n, work, weight);
- }
-
- WARN_ON_ONCE(work > weight);
-
- if (likely(work < weight))
- goto out_unlock;
+ work = __napi_poll(n, &do_repoll);
- /* Drivers must not modify the NAPI state if they
- * consume the entire weight. In such cases this code
- * still "owns" the NAPI instance and therefore can
- * move the instance around on the list at-will.
- */
- if (unlikely(napi_disable_pending(n))) {
- napi_complete(n);
+ if (!do_repoll)
goto out_unlock;
- }
-
- if (n->gro_bitmask) {
- /* flush too old packets
- * If HZ < 1000, flush all packets.
- */
- napi_gro_flush(n, HZ >= 1000);
- }
-
- gro_normal_list(n);
/* Some drivers may have called napi_schedule
* prior to exhausting their budget.
@@ -10264,6 +10323,10 @@ static int __init net_dev_init(void)
sd->backlog.weight = weight_p;
}
+ napi_workq = alloc_workqueue("napi_workq", WQ_UNBOUND | WQ_HIGHPRI,
+ WQ_UNBOUND_MAX_ACTIVE | WQ_SYSFS);
+ BUG_ON(!napi_workq);
+
dev_boot_phase = 0;
/* The loopback device is special if any other network devices
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -442,6 +442,52 @@ static ssize_t proto_down_store(struct d
}
NETDEVICE_SHOW_RW(proto_down, fmt_dec);
+static int change_napi_threaded(struct net_device *dev, unsigned long val)
+{
+ struct napi_struct *napi;
+
+ if (list_empty(&dev->napi_list))
+ return -EOPNOTSUPP;
+
+ list_for_each_entry(napi, &dev->napi_list, dev_list) {
+ if (val)
+ set_bit(NAPI_STATE_THREADED, &napi->state);
+ else
+ clear_bit(NAPI_STATE_THREADED, &napi->state);
+ }
+
+ return 0;
+}
+
+static ssize_t napi_threaded_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return netdev_store(dev, attr, buf, len, change_napi_threaded);
+}
+
+static ssize_t napi_threaded_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ struct napi_struct *napi;
+ bool enabled = false;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ list_for_each_entry(napi, &netdev->napi_list, dev_list) {
+ if (test_bit(NAPI_STATE_THREADED, &napi->state))
+ enabled = true;
+ }
+
+ rtnl_unlock();
+
+ return sprintf(buf, fmt_dec, enabled);
+}
+static DEVICE_ATTR_RW(napi_threaded);
+
static ssize_t phys_port_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -532,6 +578,7 @@ static struct attribute *net_class_attrs
&dev_attr_flags.attr,
&dev_attr_tx_queue_len.attr,
&dev_attr_gro_flush_timeout.attr,
+ &dev_attr_napi_threaded.attr,
&dev_attr_phys_port_id.attr,
&dev_attr_phys_port_name.attr,
&dev_attr_phys_switch_id.attr,