public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 net 0/2] ipv6: Fix two GC issues with permanent routes.
@ 2026-03-09 18:06 Kuniyuki Iwashima
  2026-03-09 18:06 ` [PATCH v3 net 1/2] ipv6: Remove permanent routes from tb6_gc_hlist when all exceptions expire Kuniyuki Iwashima
  2026-03-09 18:06 ` [PATCH v3 net 2/2] ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist Kuniyuki Iwashima
  0 siblings, 2 replies; 6+ messages in thread
From: Kuniyuki Iwashima @ 2026-03-09 18:06 UTC (permalink / raw)
  To: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Kui-Feng Lee, Xin Long, Simon Horman, Kuniyuki Iwashima,
	Kuniyuki Iwashima, netdev

Patch 1 fixes the unbounded growth of tb6_gc_hlist due to
permanent routes whose exception routes have all expired.

Patch 2 fixes an issue where exception routes tied to
permanent routes are not properly aged.


Changes:
  v3:
    Patch 2: Use IS_ENABLED()

  v2: https://lore.kernel.org/netdev/20260308032304.1841198-1-kuniyu@google.com/
    Patch 2: Fix build failure when CONFIG_IPV6=n (no net->ipv6 definition)

  v1: https://lore.kernel.org/netdev/20260307024709.718395-1-kuniyu@google.com/


Kuniyuki Iwashima (2):
  ipv6: Remove permanent routes from tb6_gc_hlist when all exceptions
    expire.
  ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist.

 include/net/ip6_fib.h | 19 ++++++++++++++++++-
 net/ipv6/addrconf.c   |  4 ++--
 net/ipv6/ip6_fib.c    | 15 +++++++++++++--
 net/ipv6/route.c      |  2 +-
 4 files changed, 34 insertions(+), 6 deletions(-)

-- 
2.53.0.473.g4a7958ca14-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 net 1/2] ipv6: Remove permanent routes from tb6_gc_hlist when all exceptions expire.
  2026-03-09 18:06 [PATCH v3 net 0/2] ipv6: Fix two GC issues with permanent routes Kuniyuki Iwashima
@ 2026-03-09 18:06 ` Kuniyuki Iwashima
  2026-03-10 14:08   ` Xin Long
  2026-03-09 18:06 ` [PATCH v3 net 2/2] ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist Kuniyuki Iwashima
  1 sibling, 1 reply; 6+ messages in thread
From: Kuniyuki Iwashima @ 2026-03-09 18:06 UTC (permalink / raw)
  To: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Kui-Feng Lee, Xin Long, Simon Horman, Kuniyuki Iwashima,
	Kuniyuki Iwashima, netdev

Commit 5eb902b8e719 ("net/ipv6: Remove expired routes with a
separated list of routes.") introduced a per-table GC list and
changed GC to iterate over that list instead of traversing
the entire route table.

However, it forgot to add permanent routes to tb6_gc_hlist
when exception routes are added.

Commit cfe82469a00f ("ipv6: add exception routes to GC list
in rt6_insert_exception") fixed that issue but introduced
another one.

Even after all exception routes expire, the permanent routes
remain in tb6_gc_hlist, potentially negating the performance
benefits intended by the initial change.

Let's count gc_args->more before and after rt6_age_exceptions()
and remove the permanent route when the delta is 0.

Note that the next patch will reuse fib6_age_exceptions().

Fixes: cfe82469a00f ("ipv6: add exception routes to GC list in rt6_insert_exception")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/ipv6/ip6_fib.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 9058e71241dc..fadfca49d6b1 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -2348,6 +2348,17 @@ static void fib6_flush_trees(struct net *net)
 /*
  *	Garbage collection
  */
+static void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
+				unsigned long now)
+{
+	bool may_expire = rt->fib6_flags & RTF_EXPIRES && rt->expires;
+	int old_more = gc_args->more;
+
+	rt6_age_exceptions(rt, gc_args, now);
+
+	if (!may_expire && old_more == gc_args->more)
+		fib6_remove_gc_list(rt);
+}
 
 static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
 {
@@ -2370,7 +2381,7 @@ static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
 	 *	Note, that clones are aged out
 	 *	only if they are not in use now.
 	 */
-	rt6_age_exceptions(rt, gc_args, now);
+	fib6_age_exceptions(rt, gc_args, now);
 
 	return 0;
 }
-- 
2.53.0.473.g4a7958ca14-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 net 2/2] ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist.
  2026-03-09 18:06 [PATCH v3 net 0/2] ipv6: Fix two GC issues with permanent routes Kuniyuki Iwashima
  2026-03-09 18:06 ` [PATCH v3 net 1/2] ipv6: Remove permanent routes from tb6_gc_hlist when all exceptions expire Kuniyuki Iwashima
@ 2026-03-09 18:06 ` Kuniyuki Iwashima
  2026-03-10 14:09   ` Xin Long
  1 sibling, 1 reply; 6+ messages in thread
From: Kuniyuki Iwashima @ 2026-03-09 18:06 UTC (permalink / raw)
  To: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Kui-Feng Lee, Xin Long, Simon Horman, Kuniyuki Iwashima,
	Kuniyuki Iwashima, netdev

The cited commit mechanically put fib6_remove_gc_list()
just after every fib6_clean_expires() call.

When a temporary route is promoted to a permanent route,
there may already be exception routes tied to it.

If fib6_remove_gc_list() removes the route from tb6_gc_hlist,
such exception routes will no longer be aged.

Let's replace fib6_remove_gc_list() with a new helper
fib6_may_remove_gc_list() and use fib6_age_exceptions() there.

Note that net->ipv6 is only compiled when CONFIG_IPV6 is
enabled, so fib6_{add,remove,may_remove}_gc_list() are guarded.

Fixes: 5eb902b8e719 ("net/ipv6: Remove expired routes with a separated list of routes.")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
v3: Use IS_ENABLED()
v2: Fix build failure when CONFIG_IPV6=n (no net->ipv6 definition)
---
 include/net/ip6_fib.h | 19 ++++++++++++++++++-
 net/ipv6/addrconf.c   |  4 ++--
 net/ipv6/ip6_fib.c    |  6 +++---
 net/ipv6/route.c      |  2 +-
 4 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 88b0dd4d8e09..7e91f8f35552 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -507,12 +507,14 @@ void fib6_rt_update(struct net *net, struct fib6_info *rt,
 void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
 		     unsigned int flags);
 
+void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
+			 unsigned long now);
 void fib6_run_gc(unsigned long expires, struct net *net, bool force);
-
 void fib6_gc_cleanup(void);
 
 int fib6_init(void);
 
+#if IS_ENABLED(CONFIG_IPV6)
 /* Add the route to the gc list if it is not already there
  *
  * The callers should hold f6i->fib6_table->tb6_lock.
@@ -545,6 +547,21 @@ static inline void fib6_remove_gc_list(struct fib6_info *f6i)
 		hlist_del_init(&f6i->gc_link);
 }
 
+static inline void fib6_may_remove_gc_list(struct net *net,
+					   struct fib6_info *f6i)
+{
+	struct fib6_gc_args gc_args;
+
+	if (hlist_unhashed(&f6i->gc_link))
+		return;
+
+	gc_args.timeout = READ_ONCE(net->ipv6.sysctl.ip6_rt_gc_interval),
+	gc_args.more = 0,
+
+	fib6_age_exceptions(f6i, &gc_args, jiffies);
+}
+#endif
+
 struct ipv6_route_iter {
 	struct seq_net_private p;
 	struct fib6_walker w;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 0e55f139e05d..f4e23b543585 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2862,7 +2862,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 					fib6_add_gc_list(rt);
 				} else {
 					fib6_clean_expires(rt);
-					fib6_remove_gc_list(rt);
+					fib6_may_remove_gc_list(net, rt);
 				}
 
 				spin_unlock_bh(&table->tb6_lock);
@@ -4840,7 +4840,7 @@ static int modify_prefix_route(struct net *net, struct inet6_ifaddr *ifp,
 
 		if (!(flags & RTF_EXPIRES)) {
 			fib6_clean_expires(f6i);
-			fib6_remove_gc_list(f6i);
+			fib6_may_remove_gc_list(net, f6i);
 		} else {
 			fib6_set_expires(f6i, expires);
 			fib6_add_gc_list(f6i);
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index fadfca49d6b1..dd26657b6a4a 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1133,7 +1133,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
 					return -EEXIST;
 				if (!(rt->fib6_flags & RTF_EXPIRES)) {
 					fib6_clean_expires(iter);
-					fib6_remove_gc_list(iter);
+					fib6_may_remove_gc_list(info->nl_net, iter);
 				} else {
 					fib6_set_expires(iter, rt->expires);
 					fib6_add_gc_list(iter);
@@ -2348,8 +2348,8 @@ static void fib6_flush_trees(struct net *net)
 /*
  *	Garbage collection
  */
-static void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
-				unsigned long now)
+void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
+			 unsigned long now)
 {
 	bool may_expire = rt->fib6_flags & RTF_EXPIRES && rt->expires;
 	int old_more = gc_args->more;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 08cd86f49bf9..cb521700cee7 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1033,7 +1033,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
 
 		if (!addrconf_finite_timeout(lifetime)) {
 			fib6_clean_expires(rt);
-			fib6_remove_gc_list(rt);
+			fib6_may_remove_gc_list(net, rt);
 		} else {
 			fib6_set_expires(rt, jiffies + HZ * lifetime);
 			fib6_add_gc_list(rt);
-- 
2.53.0.473.g4a7958ca14-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 net 1/2] ipv6: Remove permanent routes from tb6_gc_hlist when all exceptions expire.
  2026-03-09 18:06 ` [PATCH v3 net 1/2] ipv6: Remove permanent routes from tb6_gc_hlist when all exceptions expire Kuniyuki Iwashima
@ 2026-03-10 14:08   ` Xin Long
  0 siblings, 0 replies; 6+ messages in thread
From: Xin Long @ 2026-03-10 14:08 UTC (permalink / raw)
  To: Kuniyuki Iwashima
  Cc: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Kui-Feng Lee, Simon Horman, Kuniyuki Iwashima,
	netdev

On Mon, Mar 9, 2026 at 2:08 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
>
> Commit 5eb902b8e719 ("net/ipv6: Remove expired routes with a
> separated list of routes.") introduced a per-table GC list and
> changed GC to iterate over that list instead of traversing
> the entire route table.
>
> However, it forgot to add permanent routes to tb6_gc_hlist
> when exception routes are added.
>
> Commit cfe82469a00f ("ipv6: add exception routes to GC list
> in rt6_insert_exception") fixed that issue but introduced
> another one.
>
> Even after all exception routes expire, the permanent routes
> remain in tb6_gc_hlist, potentially negating the performance
> benefits intended by the initial change.
>
> Let's count gc_args->more before and after rt6_age_exceptions()
> and remove the permanent route when the delta is 0.
>
> Note that the next patch will reuse fib6_age_exceptions().
>
> Fixes: cfe82469a00f ("ipv6: add exception routes to GC list in rt6_insert_exception")
> Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
> ---
>  net/ipv6/ip6_fib.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
> index 9058e71241dc..fadfca49d6b1 100644
> --- a/net/ipv6/ip6_fib.c
> +++ b/net/ipv6/ip6_fib.c
> @@ -2348,6 +2348,17 @@ static void fib6_flush_trees(struct net *net)
>  /*
>   *     Garbage collection
>   */
> +static void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> +                               unsigned long now)
> +{
> +       bool may_expire = rt->fib6_flags & RTF_EXPIRES && rt->expires;
> +       int old_more = gc_args->more;
> +
> +       rt6_age_exceptions(rt, gc_args, now);
> +
> +       if (!may_expire && old_more == gc_args->more)
> +               fib6_remove_gc_list(rt);
> +}
>
>  static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
>  {
> @@ -2370,7 +2381,7 @@ static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
>          *      Note, that clones are aged out
>          *      only if they are not in use now.
>          */
> -       rt6_age_exceptions(rt, gc_args, now);
> +       fib6_age_exceptions(rt, gc_args, now);
>
>         return 0;
>  }
> --
> 2.53.0.473.g4a7958ca14-goog
>
LGTM, thanks for the fix.

Reviewed-by: Xin Long <lucien.xin@gmail.com>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 net 2/2] ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist.
  2026-03-09 18:06 ` [PATCH v3 net 2/2] ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist Kuniyuki Iwashima
@ 2026-03-10 14:09   ` Xin Long
  2026-03-10 16:01     ` Kuniyuki Iwashima
  0 siblings, 1 reply; 6+ messages in thread
From: Xin Long @ 2026-03-10 14:09 UTC (permalink / raw)
  To: Kuniyuki Iwashima
  Cc: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Kui-Feng Lee, Simon Horman, Kuniyuki Iwashima,
	netdev

On Mon, Mar 9, 2026 at 2:08 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
>
> The cited commit mechanically put fib6_remove_gc_list()
> just after every fib6_clean_expires() call.
>
> When a temporary route is promoted to a permanent route,
> there may already be exception routes tied to it.
>
> If fib6_remove_gc_list() removes the route from tb6_gc_hlist,
> such exception routes will no longer be aged.
>
> Let's replace fib6_remove_gc_list() with a new helper
> fib6_may_remove_gc_list() and use fib6_age_exceptions() there.
>
> Note that net->ipv6 is only compiled when CONFIG_IPV6 is
> enabled, so fib6_{add,remove,may_remove}_gc_list() are guarded.
>
> Fixes: 5eb902b8e719 ("net/ipv6: Remove expired routes with a separated list of routes.")
> Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
> ---
> v3: Use IS_ENABLED()
> v2: Fix build failure when CONFIG_IPV6=n (no net->ipv6 definition)
> ---
>  include/net/ip6_fib.h | 19 ++++++++++++++++++-
>  net/ipv6/addrconf.c   |  4 ++--
>  net/ipv6/ip6_fib.c    |  6 +++---
>  net/ipv6/route.c      |  2 +-
>  4 files changed, 24 insertions(+), 7 deletions(-)
>
> diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
> index 88b0dd4d8e09..7e91f8f35552 100644
> --- a/include/net/ip6_fib.h
> +++ b/include/net/ip6_fib.h
> @@ -507,12 +507,14 @@ void fib6_rt_update(struct net *net, struct fib6_info *rt,
>  void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
>                      unsigned int flags);
>
> +void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> +                        unsigned long now);
>  void fib6_run_gc(unsigned long expires, struct net *net, bool force);
> -
>  void fib6_gc_cleanup(void);
>
>  int fib6_init(void);
>
> +#if IS_ENABLED(CONFIG_IPV6)
>  /* Add the route to the gc list if it is not already there
>   *
>   * The callers should hold f6i->fib6_table->tb6_lock.
> @@ -545,6 +547,21 @@ static inline void fib6_remove_gc_list(struct fib6_info *f6i)
>                 hlist_del_init(&f6i->gc_link);
>  }
>
> +static inline void fib6_may_remove_gc_list(struct net *net,
> +                                          struct fib6_info *f6i)
> +{
> +       struct fib6_gc_args gc_args;
> +
> +       if (hlist_unhashed(&f6i->gc_link))
> +               return;
> +
> +       gc_args.timeout = READ_ONCE(net->ipv6.sysctl.ip6_rt_gc_interval),
> +       gc_args.more = 0,
Why is it using comma instead of semicolon here?

> +
> +       fib6_age_exceptions(f6i, &gc_args, jiffies);
> +}
> +#endif
> +
>  struct ipv6_route_iter {
>         struct seq_net_private p;
>         struct fib6_walker w;
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 0e55f139e05d..f4e23b543585 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2862,7 +2862,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>                                         fib6_add_gc_list(rt);
>                                 } else {
>                                         fib6_clean_expires(rt);
> -                                       fib6_remove_gc_list(rt);
> +                                       fib6_may_remove_gc_list(net, rt);
>                                 }
>
>                                 spin_unlock_bh(&table->tb6_lock);
> @@ -4840,7 +4840,7 @@ static int modify_prefix_route(struct net *net, struct inet6_ifaddr *ifp,
>
>                 if (!(flags & RTF_EXPIRES)) {
>                         fib6_clean_expires(f6i);
> -                       fib6_remove_gc_list(f6i);
> +                       fib6_may_remove_gc_list(net, f6i);
>                 } else {
>                         fib6_set_expires(f6i, expires);
>                         fib6_add_gc_list(f6i);
> diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
> index fadfca49d6b1..dd26657b6a4a 100644
> --- a/net/ipv6/ip6_fib.c
> +++ b/net/ipv6/ip6_fib.c
> @@ -1133,7 +1133,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
>                                         return -EEXIST;
>                                 if (!(rt->fib6_flags & RTF_EXPIRES)) {
>                                         fib6_clean_expires(iter);
> -                                       fib6_remove_gc_list(iter);
> +                                       fib6_may_remove_gc_list(info->nl_net, iter);
>                                 } else {
>                                         fib6_set_expires(iter, rt->expires);
>                                         fib6_add_gc_list(iter);
> @@ -2348,8 +2348,8 @@ static void fib6_flush_trees(struct net *net)
>  /*
>   *     Garbage collection
>   */
> -static void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> -                               unsigned long now)
> +void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> +                        unsigned long now)
>  {
>         bool may_expire = rt->fib6_flags & RTF_EXPIRES && rt->expires;
>         int old_more = gc_args->more;
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index 08cd86f49bf9..cb521700cee7 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -1033,7 +1033,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
>
>                 if (!addrconf_finite_timeout(lifetime)) {
>                         fib6_clean_expires(rt);
> -                       fib6_remove_gc_list(rt);
> +                       fib6_may_remove_gc_list(net, rt);
>                 } else {
>                         fib6_set_expires(rt, jiffies + HZ * lifetime);
>                         fib6_add_gc_list(rt);
> --
> 2.53.0.473.g4a7958ca14-goog
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 net 2/2] ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist.
  2026-03-10 14:09   ` Xin Long
@ 2026-03-10 16:01     ` Kuniyuki Iwashima
  0 siblings, 0 replies; 6+ messages in thread
From: Kuniyuki Iwashima @ 2026-03-10 16:01 UTC (permalink / raw)
  To: Xin Long
  Cc: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Kui-Feng Lee, Simon Horman, Kuniyuki Iwashima,
	netdev

On Tue, Mar 10, 2026 at 7:09 AM Xin Long <lucien.xin@gmail.com> wrote:
>
> On Mon, Mar 9, 2026 at 2:08 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
> >
> > The cited commit mechanically put fib6_remove_gc_list()
> > just after every fib6_clean_expires() call.
> >
> > When a temporary route is promoted to a permanent route,
> > there may already be exception routes tied to it.
> >
> > If fib6_remove_gc_list() removes the route from tb6_gc_hlist,
> > such exception routes will no longer be aged.
> >
> > Let's replace fib6_remove_gc_list() with a new helper
> > fib6_may_remove_gc_list() and use fib6_age_exceptions() there.
> >
> > Note that net->ipv6 is only compiled when CONFIG_IPV6 is
> > enabled, so fib6_{add,remove,may_remove}_gc_list() are guarded.
> >
> > Fixes: 5eb902b8e719 ("net/ipv6: Remove expired routes with a separated list of routes.")
> > Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
> > ---
> > v3: Use IS_ENABLED()
> > v2: Fix build failure when CONFIG_IPV6=n (no net->ipv6 definition)
> > ---
> >  include/net/ip6_fib.h | 19 ++++++++++++++++++-
> >  net/ipv6/addrconf.c   |  4 ++--
> >  net/ipv6/ip6_fib.c    |  6 +++---
> >  net/ipv6/route.c      |  2 +-
> >  4 files changed, 24 insertions(+), 7 deletions(-)
> >
> > diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
> > index 88b0dd4d8e09..7e91f8f35552 100644
> > --- a/include/net/ip6_fib.h
> > +++ b/include/net/ip6_fib.h
> > @@ -507,12 +507,14 @@ void fib6_rt_update(struct net *net, struct fib6_info *rt,
> >  void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
> >                      unsigned int flags);
> >
> > +void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> > +                        unsigned long now);
> >  void fib6_run_gc(unsigned long expires, struct net *net, bool force);
> > -
> >  void fib6_gc_cleanup(void);
> >
> >  int fib6_init(void);
> >
> > +#if IS_ENABLED(CONFIG_IPV6)
> >  /* Add the route to the gc list if it is not already there
> >   *
> >   * The callers should hold f6i->fib6_table->tb6_lock.
> > @@ -545,6 +547,21 @@ static inline void fib6_remove_gc_list(struct fib6_info *f6i)
> >                 hlist_del_init(&f6i->gc_link);
> >  }
> >
> > +static inline void fib6_may_remove_gc_list(struct net *net,
> > +                                          struct fib6_info *f6i)
> > +{
> > +       struct fib6_gc_args gc_args;
> > +
> > +       if (hlist_unhashed(&f6i->gc_link))
> > +               return;
> > +
> > +       gc_args.timeout = READ_ONCE(net->ipv6.sysctl.ip6_rt_gc_interval),
> > +       gc_args.more = 0,
> Why is it using comma instead of semicolon here?

Ah, my silly mistake.

I initially used designated initialiser and later moved it down
after adding hlist_unhashed(), but forgot to replace s/,/;/.

I'll update this.

Thanks for catching !

pw-bot: cr



>
> > +
> > +       fib6_age_exceptions(f6i, &gc_args, jiffies);
> > +}
> > +#endif
> > +
> >  struct ipv6_route_iter {
> >         struct seq_net_private p;
> >         struct fib6_walker w;
> > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> > index 0e55f139e05d..f4e23b543585 100644
> > --- a/net/ipv6/addrconf.c
> > +++ b/net/ipv6/addrconf.c
> > @@ -2862,7 +2862,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> >                                         fib6_add_gc_list(rt);
> >                                 } else {
> >                                         fib6_clean_expires(rt);
> > -                                       fib6_remove_gc_list(rt);
> > +                                       fib6_may_remove_gc_list(net, rt);
> >                                 }
> >
> >                                 spin_unlock_bh(&table->tb6_lock);
> > @@ -4840,7 +4840,7 @@ static int modify_prefix_route(struct net *net, struct inet6_ifaddr *ifp,
> >
> >                 if (!(flags & RTF_EXPIRES)) {
> >                         fib6_clean_expires(f6i);
> > -                       fib6_remove_gc_list(f6i);
> > +                       fib6_may_remove_gc_list(net, f6i);
> >                 } else {
> >                         fib6_set_expires(f6i, expires);
> >                         fib6_add_gc_list(f6i);
> > diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
> > index fadfca49d6b1..dd26657b6a4a 100644
> > --- a/net/ipv6/ip6_fib.c
> > +++ b/net/ipv6/ip6_fib.c
> > @@ -1133,7 +1133,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
> >                                         return -EEXIST;
> >                                 if (!(rt->fib6_flags & RTF_EXPIRES)) {
> >                                         fib6_clean_expires(iter);
> > -                                       fib6_remove_gc_list(iter);
> > +                                       fib6_may_remove_gc_list(info->nl_net, iter);
> >                                 } else {
> >                                         fib6_set_expires(iter, rt->expires);
> >                                         fib6_add_gc_list(iter);
> > @@ -2348,8 +2348,8 @@ static void fib6_flush_trees(struct net *net)
> >  /*
> >   *     Garbage collection
> >   */
> > -static void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> > -                               unsigned long now)
> > +void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> > +                        unsigned long now)
> >  {
> >         bool may_expire = rt->fib6_flags & RTF_EXPIRES && rt->expires;
> >         int old_more = gc_args->more;
> > diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> > index 08cd86f49bf9..cb521700cee7 100644
> > --- a/net/ipv6/route.c
> > +++ b/net/ipv6/route.c
> > @@ -1033,7 +1033,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
> >
> >                 if (!addrconf_finite_timeout(lifetime)) {
> >                         fib6_clean_expires(rt);
> > -                       fib6_remove_gc_list(rt);
> > +                       fib6_may_remove_gc_list(net, rt);
> >                 } else {
> >                         fib6_set_expires(rt, jiffies + HZ * lifetime);
> >                         fib6_add_gc_list(rt);
> > --
> > 2.53.0.473.g4a7958ca14-goog
> >

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-03-10 16:01 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-09 18:06 [PATCH v3 net 0/2] ipv6: Fix two GC issues with permanent routes Kuniyuki Iwashima
2026-03-09 18:06 ` [PATCH v3 net 1/2] ipv6: Remove permanent routes from tb6_gc_hlist when all exceptions expire Kuniyuki Iwashima
2026-03-10 14:08   ` Xin Long
2026-03-09 18:06 ` [PATCH v3 net 2/2] ipv6: Don't remove permanent routes with exceptions from tb6_gc_hlist Kuniyuki Iwashima
2026-03-10 14:09   ` Xin Long
2026-03-10 16:01     ` Kuniyuki Iwashima

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox