Skip to content

Commit af22d2c

Browse files
Cong Wanggregkh
authored andcommitted
l2tp: convert l2tp_tunnel_list to idr
[ Upstream commit c4d48a5 ] l2tp uses l2tp_tunnel_list to track all registered tunnels and to allocate tunnel ID's. IDR can do the same job. More importantly, with IDR we can hold the ID before a successful registration so that we don't need to worry about late error handling, it is not easy to rollback socket changes. This is a preparation for the following fix. Cc: Tetsuo Handa <[email protected]> Cc: Guillaume Nault <[email protected]> Cc: Jakub Sitnicki <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Tom Parkin <[email protected]> Signed-off-by: Cong Wang <[email protected]> Reviewed-by: Guillaume Nault <[email protected]> Signed-off-by: David S. Miller <[email protected]> Stable-dep-of: 0b2c597 ("l2tp: close all race conditions in l2tp_tunnel_register()") Signed-off-by: Sasha Levin <[email protected]>
1 parent 22c7d45 commit af22d2c

File tree

1 file changed

+42
-43
lines changed

1 file changed

+42
-43
lines changed

net/l2tp/l2tp_core.c

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ static struct workqueue_struct *l2tp_wq;
104104
/* per-net private data for this module */
105105
static unsigned int l2tp_net_id;
106106
struct l2tp_net {
107-
struct list_head l2tp_tunnel_list;
108-
/* Lock for write access to l2tp_tunnel_list */
109-
spinlock_t l2tp_tunnel_list_lock;
107+
/* Lock for write access to l2tp_tunnel_idr */
108+
spinlock_t l2tp_tunnel_idr_lock;
109+
struct idr l2tp_tunnel_idr;
110110
struct hlist_head l2tp_session_hlist[L2TP_HASH_SIZE_2];
111111
/* Lock for write access to l2tp_session_hlist */
112112
spinlock_t l2tp_session_hlist_lock;
@@ -208,13 +208,10 @@ struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
208208
struct l2tp_tunnel *tunnel;
209209

210210
rcu_read_lock_bh();
211-
list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
212-
if (tunnel->tunnel_id == tunnel_id &&
213-
refcount_inc_not_zero(&tunnel->ref_count)) {
214-
rcu_read_unlock_bh();
215-
216-
return tunnel;
217-
}
211+
tunnel = idr_find(&pn->l2tp_tunnel_idr, tunnel_id);
212+
if (tunnel && refcount_inc_not_zero(&tunnel->ref_count)) {
213+
rcu_read_unlock_bh();
214+
return tunnel;
218215
}
219216
rcu_read_unlock_bh();
220217

@@ -224,13 +221,14 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_get);
224221

225222
struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth)
226223
{
227-
const struct l2tp_net *pn = l2tp_pernet(net);
224+
struct l2tp_net *pn = l2tp_pernet(net);
225+
unsigned long tunnel_id, tmp;
228226
struct l2tp_tunnel *tunnel;
229227
int count = 0;
230228

231229
rcu_read_lock_bh();
232-
list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
233-
if (++count > nth &&
230+
idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
231+
if (tunnel && ++count > nth &&
234232
refcount_inc_not_zero(&tunnel->ref_count)) {
235233
rcu_read_unlock_bh();
236234
return tunnel;
@@ -1229,14 +1227,22 @@ static void l2tp_udp_encap_destroy(struct sock *sk)
12291227
l2tp_tunnel_delete(tunnel);
12301228
}
12311229

1230+
static void l2tp_tunnel_remove(struct net *net, struct l2tp_tunnel *tunnel)
1231+
{
1232+
struct l2tp_net *pn = l2tp_pernet(net);
1233+
1234+
spin_lock_bh(&pn->l2tp_tunnel_idr_lock);
1235+
idr_remove(&pn->l2tp_tunnel_idr, tunnel->tunnel_id);
1236+
spin_unlock_bh(&pn->l2tp_tunnel_idr_lock);
1237+
}
1238+
12321239
/* Workqueue tunnel deletion function */
12331240
static void l2tp_tunnel_del_work(struct work_struct *work)
12341241
{
12351242
struct l2tp_tunnel *tunnel = container_of(work, struct l2tp_tunnel,
12361243
del_work);
12371244
struct sock *sk = tunnel->sock;
12381245
struct socket *sock = sk->sk_socket;
1239-
struct l2tp_net *pn;
12401246

12411247
l2tp_tunnel_closeall(tunnel);
12421248

@@ -1250,12 +1256,7 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
12501256
}
12511257
}
12521258

1253-
/* Remove the tunnel struct from the tunnel list */
1254-
pn = l2tp_pernet(tunnel->l2tp_net);
1255-
spin_lock_bh(&pn->l2tp_tunnel_list_lock);
1256-
list_del_rcu(&tunnel->list);
1257-
spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
1258-
1259+
l2tp_tunnel_remove(tunnel->l2tp_net, tunnel);
12591260
/* drop initial ref */
12601261
l2tp_tunnel_dec_refcount(tunnel);
12611262

@@ -1457,12 +1458,19 @@ static int l2tp_validate_socket(const struct sock *sk, const struct net *net,
14571458
int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
14581459
struct l2tp_tunnel_cfg *cfg)
14591460
{
1460-
struct l2tp_tunnel *tunnel_walk;
1461-
struct l2tp_net *pn;
1461+
struct l2tp_net *pn = l2tp_pernet(net);
1462+
u32 tunnel_id = tunnel->tunnel_id;
14621463
struct socket *sock;
14631464
struct sock *sk;
14641465
int ret;
14651466

1467+
spin_lock_bh(&pn->l2tp_tunnel_idr_lock);
1468+
ret = idr_alloc_u32(&pn->l2tp_tunnel_idr, NULL, &tunnel_id, tunnel_id,
1469+
GFP_ATOMIC);
1470+
spin_unlock_bh(&pn->l2tp_tunnel_idr_lock);
1471+
if (ret)
1472+
return ret == -ENOSPC ? -EEXIST : ret;
1473+
14661474
if (tunnel->fd < 0) {
14671475
ret = l2tp_tunnel_sock_create(net, tunnel->tunnel_id,
14681476
tunnel->peer_tunnel_id, cfg,
@@ -1483,23 +1491,13 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
14831491
rcu_assign_sk_user_data(sk, tunnel);
14841492
write_unlock_bh(&sk->sk_callback_lock);
14851493

1486-
tunnel->l2tp_net = net;
1487-
pn = l2tp_pernet(net);
1488-
14891494
sock_hold(sk);
14901495
tunnel->sock = sk;
1496+
tunnel->l2tp_net = net;
14911497

1492-
spin_lock_bh(&pn->l2tp_tunnel_list_lock);
1493-
list_for_each_entry(tunnel_walk, &pn->l2tp_tunnel_list, list) {
1494-
if (tunnel_walk->tunnel_id == tunnel->tunnel_id) {
1495-
spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
1496-
sock_put(sk);
1497-
ret = -EEXIST;
1498-
goto err_sock;
1499-
}
1500-
}
1501-
list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list);
1502-
spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
1498+
spin_lock_bh(&pn->l2tp_tunnel_idr_lock);
1499+
idr_replace(&pn->l2tp_tunnel_idr, tunnel, tunnel->tunnel_id);
1500+
spin_unlock_bh(&pn->l2tp_tunnel_idr_lock);
15031501

15041502
if (tunnel->encap == L2TP_ENCAPTYPE_UDP) {
15051503
struct udp_tunnel_sock_cfg udp_cfg = {
@@ -1525,9 +1523,6 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
15251523

15261524
return 0;
15271525

1528-
err_sock:
1529-
write_lock_bh(&sk->sk_callback_lock);
1530-
rcu_assign_sk_user_data(sk, NULL);
15311526
err_inval_sock:
15321527
write_unlock_bh(&sk->sk_callback_lock);
15331528

@@ -1536,6 +1531,7 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
15361531
else
15371532
sockfd_put(sock);
15381533
err:
1534+
l2tp_tunnel_remove(net, tunnel);
15391535
return ret;
15401536
}
15411537
EXPORT_SYMBOL_GPL(l2tp_tunnel_register);
@@ -1649,8 +1645,8 @@ static __net_init int l2tp_init_net(struct net *net)
16491645
struct l2tp_net *pn = net_generic(net, l2tp_net_id);
16501646
int hash;
16511647

1652-
INIT_LIST_HEAD(&pn->l2tp_tunnel_list);
1653-
spin_lock_init(&pn->l2tp_tunnel_list_lock);
1648+
idr_init(&pn->l2tp_tunnel_idr);
1649+
spin_lock_init(&pn->l2tp_tunnel_idr_lock);
16541650

16551651
for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++)
16561652
INIT_HLIST_HEAD(&pn->l2tp_session_hlist[hash]);
@@ -1664,11 +1660,13 @@ static __net_exit void l2tp_exit_net(struct net *net)
16641660
{
16651661
struct l2tp_net *pn = l2tp_pernet(net);
16661662
struct l2tp_tunnel *tunnel = NULL;
1663+
unsigned long tunnel_id, tmp;
16671664
int hash;
16681665

16691666
rcu_read_lock_bh();
1670-
list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
1671-
l2tp_tunnel_delete(tunnel);
1667+
idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
1668+
if (tunnel)
1669+
l2tp_tunnel_delete(tunnel);
16721670
}
16731671
rcu_read_unlock_bh();
16741672

@@ -1678,6 +1676,7 @@ static __net_exit void l2tp_exit_net(struct net *net)
16781676

16791677
for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++)
16801678
WARN_ON_ONCE(!hlist_empty(&pn->l2tp_session_hlist[hash]));
1679+
idr_destroy(&pn->l2tp_tunnel_idr);
16811680
}
16821681

16831682
static struct pernet_operations l2tp_net_ops = {

0 commit comments

Comments
 (0)