patches/linux: port relevant kkit-6.6.y patches to kkit-6.10.y

Only the most important bridge and usb fixes have been ported in this
commit, and a new patch for the r8153 link/traffic LEDs, ported from
the FriendlyELEC/Rockchip kernel tree.

Fixes #274

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2024-08-16 16:47:28 +02:00
committed by Tobias Waldekranz
parent 01a4eedcad
commit b0c5d74860
5 changed files with 561 additions and 0 deletions
@@ -0,0 +1,236 @@
From c2251e09714b9715ee5ece18aa1096bc08cb8f6d Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 4 Mar 2024 16:47:28 +0100
Subject: [PATCH 1/5] net: bridge: avoid classifying unknown multicast as
mrouters_only
Organization: Addiva Elektronik
Unknown multicast, MAC/IPv4/IPv6, should always be flooded according to
the per-port mcast_flood setting, as well as to detected and configured
mcast_router ports.
This patch drops the mrouters_only classifier of unknown IP multicast
and moves the flow handling from br_multicast_flood() to br_flood().
This in turn means br_flood() must know about multicast router ports.
Because a multicast router should always receive both known and unknown
multicast.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
include/uapi/linux/if_bridge.h | 1 +
net/bridge/br.c | 5 +++++
net/bridge/br_device.c | 10 ++++++----
net/bridge/br_forward.c | 14 ++++++++++++--
net/bridge/br_input.c | 2 +-
net/bridge/br_multicast.c | 11 +++++++++--
net/bridge/br_private.h | 18 ++++++++++++++++--
7 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index a5b743a2f775..3bffc39d7800 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -830,6 +830,7 @@ enum br_boolopt_id {
BR_BOOLOPT_NO_LL_LEARN,
BR_BOOLOPT_MCAST_VLAN_SNOOPING,
BR_BOOLOPT_MST_ENABLE,
+ BR_BOOLOPT_MCAST_FLOOD_ALWAYS,
BR_BOOLOPT_MAX
};
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 2cab878e0a39..006fb2e5eafb 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -277,6 +277,9 @@ int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
case BR_BOOLOPT_MST_ENABLE:
err = br_mst_set_enabled(br, on, extack);
break;
+ case BR_BOOLOPT_MCAST_FLOOD_ALWAYS:
+ br_opt_toggle(br, BROPT_MCAST_FLOOD_ALWAYS, on);
+ break;
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);
@@ -295,6 +298,8 @@ int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
return br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED);
case BR_BOOLOPT_MST_ENABLE:
return br_opt_get(br, BROPT_MST_ENABLED);
+ case BR_BOOLOPT_MCAST_FLOOD_ALWAYS:
+ return br_opt_get(br, BROPT_MCAST_FLOOD_ALWAYS);
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index fb1115857e49..6aac21d86d37 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -87,10 +87,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
dest = eth_hdr(skb)->h_dest;
if (is_broadcast_ether_addr(dest)) {
- br_flood(br, skb, BR_PKT_BROADCAST, false, true, vid);
+ br_flood(br, skb, NULL, BR_PKT_BROADCAST, false, true, vid);
} else if (is_multicast_ether_addr(dest)) {
if (unlikely(netpoll_tx_running(dev))) {
- br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid);
+ br_flood(br, skb, brmctx, BR_PKT_MULTICAST, false, true, vid);
goto out;
}
if (br_multicast_rcv(&brmctx, &pmctx_null, vlan, skb, vid)) {
@@ -103,11 +103,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst))
br_multicast_flood(mdst, skb, brmctx, false, true);
else
- br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid);
+ br_flood(br, skb, brmctx, BR_PKT_MULTICAST, false, true, vid);
} else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) {
br_forward(dst->dst, skb, false, true);
} else {
- br_flood(br, skb, BR_PKT_UNICAST, false, true, vid);
+ br_flood(br, skb, NULL, BR_PKT_UNICAST, false, true, vid);
}
out:
rcu_read_unlock();
@@ -524,6 +524,8 @@ void br_dev_setup(struct net_device *dev)
br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME;
dev->max_mtu = ETH_MAX_MTU;
+ br_opt_toggle(br, BROPT_MCAST_FLOOD_ALWAYS, false);
+
br_netfilter_rtable_init(br);
br_stp_timer_init(br);
br_multicast_init(br);
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index e19b583ff2c6..1094364d96e0 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -198,14 +198,19 @@ static struct net_bridge_port *maybe_deliver(
/* called under rcu_read_lock */
void br_flood(struct net_bridge *br, struct sk_buff *skb,
- enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
- u16 vid)
+ struct net_bridge_mcast *brmctx, enum br_pkt_type pkt_type,
+ bool local_rcv, bool local_orig, u16 vid)
{
+ struct net_bridge_port *rport = NULL;
struct net_bridge_port *prev = NULL;
+ struct hlist_node *rp = NULL;
struct net_bridge_port *p;
br_tc_skb_miss_set(skb, pkt_type != BR_PKT_BROADCAST);
+ if (pkt_type == BR_PKT_MULTICAST)
+ rp = br_multicast_get_first_rport_node(brmctx, skb);
+
list_for_each_entry_rcu(p, &br->port_list, list) {
/* Do not flood unicast traffic to ports that turn it off, nor
* other traffic if flood off, except for traffic we originate
@@ -216,6 +221,11 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb,
continue;
break;
case BR_PKT_MULTICAST:
+ rport = br_multicast_rport_from_node_skb(rp, skb);
+ if (rport == p) {
+ rp = rcu_dereference(hlist_next_rcu(rp));
+ break;
+ }
if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
continue;
break;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index ceaa5a89b947..e54d648db7de 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -212,7 +212,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
br_forward(dst->dst, skb, local_rcv, false);
} else {
if (!mcast_hit)
- br_flood(br, skb, pkt_type, local_rcv, false, vid);
+ br_flood(br, skb, brmctx, pkt_type, local_rcv, false, vid);
else
br_multicast_flood(mdst, skb, brmctx, local_rcv, false);
}
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 9a1cb5079a7a..dc49df4f92a8 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3776,6 +3776,11 @@ static void br_multicast_err_count(const struct net_bridge *br,
u64_stats_update_end(&pstats->syncp);
}
+static bool br_flood_mrouters(const struct net_bridge *br)
+{
+ return br_opt_get(br, BROPT_MCAST_FLOOD_ALWAYS) ? false : true;
+}
+
static void br_multicast_pim(struct net_bridge_mcast *brmctx,
struct net_bridge_mcast_port *pmctx,
const struct sk_buff *skb)
@@ -3822,7 +3827,8 @@ static int br_multicast_ipv4_rcv(struct net_bridge_mcast *brmctx,
if (err == -ENOMSG) {
if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) {
- BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
+ BR_INPUT_SKB_CB(skb)->mrouters_only =
+ br_flood_mrouters(brmctx->br);
} else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) {
if (ip_hdr(skb)->protocol == IPPROTO_PIM)
br_multicast_pim(brmctx, pmctx, skb);
@@ -3891,7 +3897,8 @@ static int br_multicast_ipv6_rcv(struct net_bridge_mcast *brmctx,
if (err == -ENOMSG || err == -ENODATA) {
if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
- BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
+ BR_INPUT_SKB_CB(skb)->mrouters_only =
+ br_flood_mrouters(brmctx->br);
if (err == -ENODATA &&
ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr))
br_ip6_multicast_mrd_rcv(brmctx, pmctx, skb);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d4bedc87b1d8..dfd6e2cdd13e 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -483,6 +483,7 @@ enum net_bridge_opts {
BROPT_VLAN_BRIDGE_BINDING,
BROPT_MCAST_VLAN_SNOOPING_ENABLED,
BROPT_MST_ENABLED,
+ BROPT_MCAST_FLOOD_ALWAYS,
};
struct net_bridge {
@@ -886,8 +887,8 @@ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb,
bool local_rcv, bool local_orig);
int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
void br_flood(struct net_bridge *br, struct sk_buff *skb,
- enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
- u16 vid);
+ struct net_bridge_mcast *brmctx, enum br_pkt_type pkt_type,
+ bool local_rcv, bool local_orig, u16 vid);
/* return true if both source port and dest port are isolated */
static inline bool br_skb_isolated(const struct net_bridge_port *to,
@@ -1408,6 +1409,19 @@ static inline void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
{
}
+static inline struct hlist_node *
+br_multicast_get_first_rport_node(struct net_bridge_mcast *brmctx,
+ struct sk_buff *skb)
+{
+ return NULL;
+}
+
+static inline struct net_bridge_port *
+br_multicast_rport_from_node_skb(struct hlist_node *rp, struct sk_buff *skb)
+{
+ return NULL;
+}
+
static inline bool br_multicast_is_router(struct net_bridge_mcast *brmctx,
struct sk_buff *skb)
{
--
2.43.0
@@ -0,0 +1,37 @@
From 75edfa9b690b65423aa3e63ed5870d565950d9bb Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Tue, 5 Mar 2024 06:44:41 +0100
Subject: [PATCH 2/5] net: bridge: Ignore router ports when forwarding L2
multicast
Organization: Addiva Elektronik
Multicast router ports are either statically configured or learned from
control protocol traffic (IGMP/MLD/PIM). These protocols regulate IP
multicast -- MAC multicast should always be forwarded through flooding
of unknown multicast or using permanent MDB entries.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
net/bridge/br_private.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index dfd6e2cdd13e..7aabf8bd7da2 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1086,7 +1086,10 @@ br_multicast_get_first_rport_node(struct net_bridge_mcast *brmctx,
if (skb->protocol == htons(ETH_P_IPV6))
return rcu_dereference(hlist_first_rcu(&brmctx->ip6_mc_router_list));
#endif
- return rcu_dereference(hlist_first_rcu(&brmctx->ip4_mc_router_list));
+ if (skb->protocol == htons(ETH_P_IP))
+ return rcu_dereference(hlist_first_rcu(&brmctx->ip4_mc_router_list));
+
+ return NULL;
}
static inline struct net_bridge_port *
--
2.43.0
@@ -0,0 +1,190 @@
From 2d2ac57a7d9354c2c1fa796659e33496fc8e918d Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Thu, 4 Apr 2024 16:36:30 +0200
Subject: [PATCH 3/5] net: bridge: drop delay for applying strict multicast
filtering
Organization: Addiva Elektronik
This *local* patch drops the initial delay before applying strict multicast
filtering, introduced in [1] and recently updated in [2].
The main reason for this patch is RFC conformance and customer expectations.
At power on we assume the querier role by default and are expected to stop
unknown flooding as soon as we have a membership report in place, not after
10 seconds.
A proper fix for upstreaming could be to add a knob to disable the delay.
[1]: https://lore.kernel.org/netdev/1375311980-25575-1-git-send-email-linus.luessing@web.de/
[2]: https://lore.kernel.org/netdev/20240127175033.9640-1-linus.luessing@c0d3.blue/
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
net/bridge/br_multicast.c | 42 +++++++--------------------------------
net/bridge/br_private.h | 4 +---
2 files changed, 8 insertions(+), 38 deletions(-)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index dc49df4f92a8..47c0625c5553 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1762,10 +1762,6 @@ static void br_ip6_multicast_querier_expired(struct timer_list *t)
}
#endif
-static void br_multicast_query_delay_expired(struct timer_list *t)
-{
-}
-
static void br_multicast_select_own_querier(struct net_bridge_mcast *brmctx,
struct br_ip *ip,
struct sk_buff *skb)
@@ -3198,12 +3194,8 @@ int br_multicast_dump_querier_state(struct sk_buff *skb,
static void
br_multicast_update_query_timer(struct net_bridge_mcast *brmctx,
- struct bridge_mcast_other_query *query,
- unsigned long max_delay)
+ struct bridge_mcast_other_query *query)
{
- if (!timer_pending(&query->timer))
- mod_timer(&query->delay_timer, jiffies + max_delay);
-
mod_timer(&query->timer, jiffies + brmctx->multicast_querier_interval);
}
@@ -3394,13 +3386,12 @@ static void
br_ip4_multicast_query_received(struct net_bridge_mcast *brmctx,
struct net_bridge_mcast_port *pmctx,
struct bridge_mcast_other_query *query,
- struct br_ip *saddr,
- unsigned long max_delay)
+ struct br_ip *saddr)
{
if (!br_multicast_select_querier(brmctx, pmctx, saddr))
return;
- br_multicast_update_query_timer(brmctx, query, max_delay);
+ br_multicast_update_query_timer(brmctx, query);
br_ip4_multicast_mark_router(brmctx, pmctx);
}
@@ -3409,13 +3400,12 @@ static void
br_ip6_multicast_query_received(struct net_bridge_mcast *brmctx,
struct net_bridge_mcast_port *pmctx,
struct bridge_mcast_other_query *query,
- struct br_ip *saddr,
- unsigned long max_delay)
+ struct br_ip *saddr)
{
if (!br_multicast_select_querier(brmctx, pmctx, saddr))
return;
- br_multicast_update_query_timer(brmctx, query, max_delay);
+ br_multicast_update_query_timer(brmctx, query);
br_ip6_multicast_mark_router(brmctx, pmctx);
}
#endif
@@ -3469,7 +3459,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
br_ip4_multicast_query_received(brmctx, pmctx,
&brmctx->ip4_other_query,
- &saddr, max_delay);
+ &saddr);
goto out;
}
@@ -3557,7 +3547,7 @@ static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx,
br_ip6_multicast_query_received(brmctx, pmctx,
&brmctx->ip6_other_query,
- &saddr, max_delay);
+ &saddr);
goto out;
} else if (!group) {
goto out;
@@ -4065,8 +4055,6 @@ void br_multicast_ctx_init(struct net_bridge *br,
br_ip4_multicast_local_router_expired, 0);
timer_setup(&brmctx->ip4_other_query.timer,
br_ip4_multicast_querier_expired, 0);
- timer_setup(&brmctx->ip4_other_query.delay_timer,
- br_multicast_query_delay_expired, 0);
timer_setup(&brmctx->ip4_own_query.timer,
br_ip4_multicast_query_expired, 0);
#if IS_ENABLED(CONFIG_IPV6)
@@ -4074,8 +4062,6 @@ void br_multicast_ctx_init(struct net_bridge *br,
br_ip6_multicast_local_router_expired, 0);
timer_setup(&brmctx->ip6_other_query.timer,
br_ip6_multicast_querier_expired, 0);
- timer_setup(&brmctx->ip6_other_query.delay_timer,
- br_multicast_query_delay_expired, 0);
timer_setup(&brmctx->ip6_own_query.timer,
br_ip6_multicast_query_expired, 0);
#endif
@@ -4210,12 +4196,10 @@ static void __br_multicast_stop(struct net_bridge_mcast *brmctx)
{
del_timer_sync(&brmctx->ip4_mc_router_timer);
del_timer_sync(&brmctx->ip4_other_query.timer);
- del_timer_sync(&brmctx->ip4_other_query.delay_timer);
del_timer_sync(&brmctx->ip4_own_query.timer);
#if IS_ENABLED(CONFIG_IPV6)
del_timer_sync(&brmctx->ip6_mc_router_timer);
del_timer_sync(&brmctx->ip6_other_query.timer);
- del_timer_sync(&brmctx->ip6_other_query.delay_timer);
del_timer_sync(&brmctx->ip6_own_query.timer);
#endif
}
@@ -4643,8 +4627,6 @@ EXPORT_SYMBOL_GPL(br_multicast_router);
int br_multicast_set_querier(struct net_bridge_mcast *brmctx, unsigned long val)
{
- unsigned long max_delay;
-
val = !!val;
spin_lock_bh(&brmctx->br->multicast_lock);
@@ -4655,19 +4637,9 @@ int br_multicast_set_querier(struct net_bridge_mcast *brmctx, unsigned long val)
if (!val)
goto unlock;
- max_delay = brmctx->multicast_query_response_interval;
-
- if (!timer_pending(&brmctx->ip4_other_query.timer))
- mod_timer(&brmctx->ip4_other_query.delay_timer,
- jiffies + max_delay);
-
br_multicast_start_querier(brmctx, &brmctx->ip4_own_query);
#if IS_ENABLED(CONFIG_IPV6)
- if (!timer_pending(&brmctx->ip6_other_query.timer))
- mod_timer(&brmctx->ip6_other_query.delay_timer,
- jiffies + max_delay);
-
br_multicast_start_querier(brmctx, &brmctx->ip6_own_query);
#endif
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 7aabf8bd7da2..43fff09dfb71 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -78,7 +78,6 @@ struct bridge_mcast_own_query {
/* other querier */
struct bridge_mcast_other_query {
struct timer_list timer;
- struct timer_list delay_timer;
};
/* selected querier */
@@ -1164,8 +1163,7 @@ __br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
own_querier_enabled = false;
}
- return !timer_pending(&querier->delay_timer) &&
- (own_querier_enabled || timer_pending(&querier->timer));
+ return own_querier_enabled || timer_pending(&querier->timer);
}
static inline bool br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
--
2.43.0
@@ -0,0 +1,49 @@
From c7818c4273ef1705d6e153f5e4eda683d68f59be Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 29 Apr 2024 15:14:51 +0200
Subject: [PATCH 4/5] usb: core: adjust log level for unauthorized devices
Organization: Addiva Elektronik
The fact that a USB device currently is not authorized is not an error,
so let's adjust the log level so these messages slip below radar for the
commonly used 'quiet' log level.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/usb/core/driver.c | 4 ++--
drivers/usb/core/generic.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index e02ba15f6e34..3fe4d15cd6db 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -335,10 +335,10 @@ static int usb_probe_interface(struct device *dev)
return error;
if (udev->authorized == 0) {
- dev_err(&intf->dev, "Device is not authorized for usage\n");
+ dev_warn(&intf->dev, "Device is not authorized for usage\n");
return error;
} else if (intf->authorized == 0) {
- dev_err(&intf->dev, "Interface %d is not authorized for usage\n",
+ dev_warn(&intf->dev, "Interface %d is not authorized for usage\n",
intf->altsetting->desc.bInterfaceNumber);
return error;
}
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index b134bff5c3fe..60575a01a810 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -247,7 +247,7 @@ int usb_generic_driver_probe(struct usb_device *udev)
* with the driver core and lets interface drivers bind to them.
*/
if (udev->authorized == 0)
- dev_err(&udev->dev, "Device is not authorized for usage\n");
+ dev_warn(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
--
2.43.0
@@ -0,0 +1,49 @@
From daba70e7782b704224396f0d86dcd91b4713e064 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Sun, 11 Aug 2024 11:27:35 +0200
Subject: [PATCH 5/5] net: usb: r8152: add r8153b support for link/activity
LEDs
Organization: Addiva Elektronik
This patch adds support for the link/activity LEDs on the NanoPi R2S
and OrangePi R1 Plus.
From the Rockchip kernel hosted by FriendlyARM/FriendlyELEC at
https://github.com/friendlyarm/kernel-rockchip/
Specifically commit be0b1939b271c307ab1fef5fb1478beaafc6de17
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/net/usb/r8152.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 19df1cd9f072..810477991810 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -41,6 +41,11 @@
#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters"
#define MODULENAME "r8152"
+/* LED0: Activity, LED1: Link */
+static int ledsel = 0x78;
+module_param(ledsel, int, 0);
+MODULE_PARM_DESC(ledsel, "Override default LED configuration");
+
#define R8152_PHY_ID 32
#define PLA_IDR 0xc000
@@ -7296,6 +7301,9 @@ static void r8153b_init(struct r8152 *tp)
ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+ /* set customized led */
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LEDSEL, ledsel);
+
rtl_tally_reset(tp);
tp->coalesce = 15000; /* 15 us */
--
2.43.0