Skip to content

Commit 07b87f9

Browse files
committed
xfrm: Fix unregister netdevice hang on hardware offload.
When offloading xfrm states to hardware, the offloading device is attached to the skbs secpath. If a skb is free is deferred, an unregister netdevice hangs because the netdevice is still refcounted. Fix this by removing the netdevice from the xfrm states when the netdevice is unregistered. To find all xfrm states that need to be cleared we add another list where skbs linked to that are unlinked from the lists (deleted) but not yet freed. Fixes: d77e38e ("xfrm: Add an IPsec hardware offloading API") Signed-off-by: Steffen Klassert <[email protected]>
1 parent 15f5fe9 commit 07b87f9

File tree

2 files changed

+69
-28
lines changed

2 files changed

+69
-28
lines changed

include/net/xfrm.h

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,10 @@ struct xfrm_state {
178178
struct hlist_node gclist;
179179
struct hlist_node bydst;
180180
};
181-
struct hlist_node bysrc;
181+
union {
182+
struct hlist_node dev_gclist;
183+
struct hlist_node bysrc;
184+
};
182185
struct hlist_node byspi;
183186
struct hlist_node byseq;
184187

@@ -1588,7 +1591,7 @@ void xfrm_state_update_stats(struct net *net);
15881591
static inline void xfrm_dev_state_update_stats(struct xfrm_state *x)
15891592
{
15901593
struct xfrm_dev_offload *xdo = &x->xso;
1591-
struct net_device *dev = xdo->dev;
1594+
struct net_device *dev = READ_ONCE(xdo->dev);
15921595

15931596
if (dev && dev->xfrmdev_ops &&
15941597
dev->xfrmdev_ops->xdo_dev_state_update_stats)
@@ -1946,13 +1949,16 @@ int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
19461949
struct xfrm_user_offload *xuo, u8 dir,
19471950
struct netlink_ext_ack *extack);
19481951
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
1952+
void xfrm_dev_state_delete(struct xfrm_state *x);
1953+
void xfrm_dev_state_free(struct xfrm_state *x);
19491954

19501955
static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
19511956
{
19521957
struct xfrm_dev_offload *xso = &x->xso;
1958+
struct net_device *dev = READ_ONCE(xso->dev);
19531959

1954-
if (xso->dev && xso->dev->xfrmdev_ops->xdo_dev_state_advance_esn)
1955-
xso->dev->xfrmdev_ops->xdo_dev_state_advance_esn(x);
1960+
if (dev && dev->xfrmdev_ops->xdo_dev_state_advance_esn)
1961+
dev->xfrmdev_ops->xdo_dev_state_advance_esn(x);
19561962
}
19571963

19581964
static inline bool xfrm_dst_offload_ok(struct dst_entry *dst)
@@ -1973,28 +1979,6 @@ static inline bool xfrm_dst_offload_ok(struct dst_entry *dst)
19731979
return false;
19741980
}
19751981

1976-
static inline void xfrm_dev_state_delete(struct xfrm_state *x)
1977-
{
1978-
struct xfrm_dev_offload *xso = &x->xso;
1979-
1980-
if (xso->dev)
1981-
xso->dev->xfrmdev_ops->xdo_dev_state_delete(x);
1982-
}
1983-
1984-
static inline void xfrm_dev_state_free(struct xfrm_state *x)
1985-
{
1986-
struct xfrm_dev_offload *xso = &x->xso;
1987-
struct net_device *dev = xso->dev;
1988-
1989-
if (dev && dev->xfrmdev_ops) {
1990-
if (dev->xfrmdev_ops->xdo_dev_state_free)
1991-
dev->xfrmdev_ops->xdo_dev_state_free(x);
1992-
xso->dev = NULL;
1993-
xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
1994-
netdev_put(dev, &xso->dev_tracker);
1995-
}
1996-
}
1997-
19981982
static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
19991983
{
20001984
struct xfrm_dev_offload *xdo = &x->xdo;

net/xfrm/xfrm_state.c

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ static struct kmem_cache *xfrm_state_cache __ro_after_init;
4949

5050
static DECLARE_WORK(xfrm_state_gc_work, xfrm_state_gc_task);
5151
static HLIST_HEAD(xfrm_state_gc_list);
52+
static HLIST_HEAD(xfrm_state_dev_gc_list);
5253

5354
static inline bool xfrm_state_hold_rcu(struct xfrm_state __rcu *x)
5455
{
@@ -214,6 +215,7 @@ static DEFINE_SPINLOCK(xfrm_state_afinfo_lock);
214215
static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO];
215216

216217
static DEFINE_SPINLOCK(xfrm_state_gc_lock);
218+
static DEFINE_SPINLOCK(xfrm_state_dev_gc_lock);
217219

218220
int __xfrm_state_delete(struct xfrm_state *x);
219221

@@ -683,6 +685,40 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
683685
}
684686
EXPORT_SYMBOL(xfrm_state_alloc);
685687

688+
#ifdef CONFIG_XFRM_OFFLOAD
689+
void xfrm_dev_state_delete(struct xfrm_state *x)
690+
{
691+
struct xfrm_dev_offload *xso = &x->xso;
692+
struct net_device *dev = READ_ONCE(xso->dev);
693+
694+
if (dev) {
695+
dev->xfrmdev_ops->xdo_dev_state_delete(x);
696+
spin_lock_bh(&xfrm_state_dev_gc_lock);
697+
hlist_add_head(&x->dev_gclist, &xfrm_state_dev_gc_list);
698+
spin_unlock_bh(&xfrm_state_dev_gc_lock);
699+
}
700+
}
701+
702+
void xfrm_dev_state_free(struct xfrm_state *x)
703+
{
704+
struct xfrm_dev_offload *xso = &x->xso;
705+
struct net_device *dev = READ_ONCE(xso->dev);
706+
707+
if (dev && dev->xfrmdev_ops) {
708+
spin_lock_bh(&xfrm_state_dev_gc_lock);
709+
if (!hlist_unhashed(&x->dev_gclist))
710+
hlist_del(&x->dev_gclist);
711+
spin_unlock_bh(&xfrm_state_dev_gc_lock);
712+
713+
if (dev->xfrmdev_ops->xdo_dev_state_free)
714+
dev->xfrmdev_ops->xdo_dev_state_free(x);
715+
WRITE_ONCE(xso->dev, NULL);
716+
xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
717+
netdev_put(dev, &xso->dev_tracker);
718+
}
719+
}
720+
#endif
721+
686722
void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
687723
{
688724
WARN_ON(x->km.state != XFRM_STATE_DEAD);
@@ -848,6 +884,9 @@ EXPORT_SYMBOL(xfrm_state_flush);
848884

849885
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid)
850886
{
887+
struct xfrm_state *x;
888+
struct hlist_node *tmp;
889+
struct xfrm_dev_offload *xso;
851890
int i, err = 0, cnt = 0;
852891

853892
spin_lock_bh(&net->xfrm.xfrm_state_lock);
@@ -857,8 +896,6 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali
857896

858897
err = -ESRCH;
859898
for (i = 0; i <= net->xfrm.state_hmask; i++) {
860-
struct xfrm_state *x;
861-
struct xfrm_dev_offload *xso;
862899
restart:
863900
hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
864901
xso = &x->xso;
@@ -868,6 +905,8 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali
868905
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
869906

870907
err = xfrm_state_delete(x);
908+
xfrm_dev_state_free(x);
909+
871910
xfrm_audit_state_delete(x, err ? 0 : 1,
872911
task_valid);
873912
xfrm_state_put(x);
@@ -884,6 +923,24 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali
884923

885924
out:
886925
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
926+
927+
spin_lock_bh(&xfrm_state_dev_gc_lock);
928+
restart_gc:
929+
hlist_for_each_entry_safe(x, tmp, &xfrm_state_dev_gc_list, dev_gclist) {
930+
xso = &x->xso;
931+
932+
if (xso->dev == dev) {
933+
spin_unlock_bh(&xfrm_state_dev_gc_lock);
934+
xfrm_dev_state_free(x);
935+
spin_lock_bh(&xfrm_state_dev_gc_lock);
936+
goto restart_gc;
937+
}
938+
939+
}
940+
spin_unlock_bh(&xfrm_state_dev_gc_lock);
941+
942+
xfrm_flush_gc();
943+
887944
return err;
888945
}
889946
EXPORT_SYMBOL(xfrm_dev_state_flush);

0 commit comments

Comments
 (0)