Skip to content

Commit 72cdc67

Browse files
LGA1150kuba-moo
authored andcommitted
pppoe: remove rwlock usage
Like ppp_generic.c, convert the PPPoE socket hash table to use RCU for lookups and a spinlock for updates. This removes rwlock usage and allows lockless readers on the fast path. - Mark hash table and list pointers as __rcu. - Use spin_lock() to protect writers. - Readers use rcu_dereference() under rcu_read_lock(). All known callers of get_item() already hold the RCU read lock, so no additional locking is needed. - get_item() now uses refcount_inc_not_zero() instead of sock_hold() to safely take a reference. This prevents crashes if a socket is already in the process of being freed (sk_refcnt == 0). - Set SOCK_RCU_FREE to defer socket freeing until after an RCU grace period. - Move skb_queue_purge() into sk_destruct callback to ensure purge happens after an RCU grace period. Signed-off-by: Qingfang Deng <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent d23ad54 commit 72cdc67

File tree

2 files changed

+54
-42
lines changed

2 files changed

+54
-42
lines changed

drivers/net/ppp/pppoe.c

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ struct pppoe_net {
100100
* as well, moreover in case of SMP less locking
101101
* controversy here
102102
*/
103-
struct pppox_sock *hash_table[PPPOE_HASH_SIZE];
104-
rwlock_t hash_lock;
103+
struct pppox_sock __rcu *hash_table[PPPOE_HASH_SIZE];
104+
spinlock_t hash_lock;
105105
};
106106

107107
/*
@@ -162,13 +162,13 @@ static struct pppox_sock *__get_item(struct pppoe_net *pn, __be16 sid,
162162
int hash = hash_item(sid, addr);
163163
struct pppox_sock *ret;
164164

165-
ret = pn->hash_table[hash];
165+
ret = rcu_dereference(pn->hash_table[hash]);
166166
while (ret) {
167167
if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
168168
ret->pppoe_ifindex == ifindex)
169169
return ret;
170170

171-
ret = ret->next;
171+
ret = rcu_dereference(ret->next);
172172
}
173173

174174
return NULL;
@@ -177,19 +177,20 @@ static struct pppox_sock *__get_item(struct pppoe_net *pn, __be16 sid,
177177
static int __set_item(struct pppoe_net *pn, struct pppox_sock *po)
178178
{
179179
int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
180-
struct pppox_sock *ret;
180+
struct pppox_sock *ret, *first;
181181

182-
ret = pn->hash_table[hash];
182+
first = rcu_dereference_protected(pn->hash_table[hash], lockdep_is_held(&pn->hash_lock));
183+
ret = first;
183184
while (ret) {
184185
if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) &&
185186
ret->pppoe_ifindex == po->pppoe_ifindex)
186187
return -EALREADY;
187188

188-
ret = ret->next;
189+
ret = rcu_dereference_protected(ret->next, lockdep_is_held(&pn->hash_lock));
189190
}
190191

191-
po->next = pn->hash_table[hash];
192-
pn->hash_table[hash] = po;
192+
RCU_INIT_POINTER(po->next, first);
193+
rcu_assign_pointer(pn->hash_table[hash], po);
193194

194195
return 0;
195196
}
@@ -198,20 +199,24 @@ static void __delete_item(struct pppoe_net *pn, __be16 sid,
198199
char *addr, int ifindex)
199200
{
200201
int hash = hash_item(sid, addr);
201-
struct pppox_sock *ret, **src;
202+
struct pppox_sock *ret, __rcu **src;
202203

203-
ret = pn->hash_table[hash];
204+
ret = rcu_dereference_protected(pn->hash_table[hash], lockdep_is_held(&pn->hash_lock));
204205
src = &pn->hash_table[hash];
205206

206207
while (ret) {
207208
if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
208209
ret->pppoe_ifindex == ifindex) {
209-
*src = ret->next;
210+
struct pppox_sock *next;
211+
212+
next = rcu_dereference_protected(ret->next,
213+
lockdep_is_held(&pn->hash_lock));
214+
rcu_assign_pointer(*src, next);
210215
break;
211216
}
212217

213218
src = &ret->next;
214-
ret = ret->next;
219+
ret = rcu_dereference_protected(ret->next, lockdep_is_held(&pn->hash_lock));
215220
}
216221
}
217222

@@ -225,11 +230,9 @@ static inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid,
225230
{
226231
struct pppox_sock *po;
227232

228-
read_lock_bh(&pn->hash_lock);
229233
po = __get_item(pn, sid, addr, ifindex);
230-
if (po)
231-
sock_hold(sk_pppox(po));
232-
read_unlock_bh(&pn->hash_lock);
234+
if (po && !refcount_inc_not_zero(&sk_pppox(po)->sk_refcnt))
235+
po = NULL;
233236

234237
return po;
235238
}
@@ -258,9 +261,9 @@ static inline struct pppox_sock *get_item_by_addr(struct net *net,
258261
static inline void delete_item(struct pppoe_net *pn, __be16 sid,
259262
char *addr, int ifindex)
260263
{
261-
write_lock_bh(&pn->hash_lock);
264+
spin_lock(&pn->hash_lock);
262265
__delete_item(pn, sid, addr, ifindex);
263-
write_unlock_bh(&pn->hash_lock);
266+
spin_unlock(&pn->hash_lock);
264267
}
265268

266269
/***************************************************************************
@@ -276,14 +279,16 @@ static void pppoe_flush_dev(struct net_device *dev)
276279
int i;
277280

278281
pn = pppoe_pernet(dev_net(dev));
279-
write_lock_bh(&pn->hash_lock);
282+
spin_lock(&pn->hash_lock);
280283
for (i = 0; i < PPPOE_HASH_SIZE; i++) {
281-
struct pppox_sock *po = pn->hash_table[i];
284+
struct pppox_sock *po = rcu_dereference_protected(pn->hash_table[i],
285+
lockdep_is_held(&pn->hash_lock));
282286
struct sock *sk;
283287

284288
while (po) {
285289
while (po && po->pppoe_dev != dev) {
286-
po = po->next;
290+
po = rcu_dereference_protected(po->next,
291+
lockdep_is_held(&pn->hash_lock));
287292
}
288293

289294
if (!po)
@@ -300,7 +305,7 @@ static void pppoe_flush_dev(struct net_device *dev)
300305
*/
301306

302307
sock_hold(sk);
303-
write_unlock_bh(&pn->hash_lock);
308+
spin_unlock(&pn->hash_lock);
304309
lock_sock(sk);
305310

306311
if (po->pppoe_dev == dev &&
@@ -320,11 +325,12 @@ static void pppoe_flush_dev(struct net_device *dev)
320325
*/
321326

322327
BUG_ON(pppoe_pernet(dev_net(dev)) == NULL);
323-
write_lock_bh(&pn->hash_lock);
324-
po = pn->hash_table[i];
328+
spin_lock(&pn->hash_lock);
329+
po = rcu_dereference_protected(pn->hash_table[i],
330+
lockdep_is_held(&pn->hash_lock));
325331
}
326332
}
327-
write_unlock_bh(&pn->hash_lock);
333+
spin_unlock(&pn->hash_lock);
328334
}
329335

330336
static int pppoe_device_event(struct notifier_block *this,
@@ -528,6 +534,11 @@ static struct proto pppoe_sk_proto __read_mostly = {
528534
.obj_size = sizeof(struct pppox_sock),
529535
};
530536

537+
static void pppoe_destruct(struct sock *sk)
538+
{
539+
skb_queue_purge(&sk->sk_receive_queue);
540+
}
541+
531542
/***********************************************************************
532543
*
533544
* Initialize a new struct sock.
@@ -542,11 +553,13 @@ static int pppoe_create(struct net *net, struct socket *sock, int kern)
542553
return -ENOMEM;
543554

544555
sock_init_data(sock, sk);
556+
sock_set_flag(sk, SOCK_RCU_FREE);
545557

546558
sock->state = SS_UNCONNECTED;
547559
sock->ops = &pppoe_ops;
548560

549561
sk->sk_backlog_rcv = pppoe_rcv_core;
562+
sk->sk_destruct = pppoe_destruct;
550563
sk->sk_state = PPPOX_NONE;
551564
sk->sk_type = SOCK_STREAM;
552565
sk->sk_family = PF_PPPOX;
@@ -599,7 +612,6 @@ static int pppoe_release(struct socket *sock)
599612
sock_orphan(sk);
600613
sock->sk = NULL;
601614

602-
skb_queue_purge(&sk->sk_receive_queue);
603615
release_sock(sk);
604616
sock_put(sk);
605617

@@ -681,9 +693,9 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
681693
&sp->sa_addr.pppoe,
682694
sizeof(struct pppoe_addr));
683695

684-
write_lock_bh(&pn->hash_lock);
696+
spin_lock(&pn->hash_lock);
685697
error = __set_item(pn, po);
686-
write_unlock_bh(&pn->hash_lock);
698+
spin_unlock(&pn->hash_lock);
687699
if (error < 0)
688700
goto err_put;
689701

@@ -1052,11 +1064,11 @@ static inline struct pppox_sock *pppoe_get_idx(struct pppoe_net *pn, loff_t pos)
10521064
int i;
10531065

10541066
for (i = 0; i < PPPOE_HASH_SIZE; i++) {
1055-
po = pn->hash_table[i];
1067+
po = rcu_dereference(pn->hash_table[i]);
10561068
while (po) {
10571069
if (!pos--)
10581070
goto out;
1059-
po = po->next;
1071+
po = rcu_dereference(po->next);
10601072
}
10611073
}
10621074

@@ -1065,34 +1077,35 @@ static inline struct pppox_sock *pppoe_get_idx(struct pppoe_net *pn, loff_t pos)
10651077
}
10661078

10671079
static void *pppoe_seq_start(struct seq_file *seq, loff_t *pos)
1068-
__acquires(pn->hash_lock)
1080+
__acquires(RCU)
10691081
{
10701082
struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
10711083
loff_t l = *pos;
10721084

1073-
read_lock_bh(&pn->hash_lock);
1085+
rcu_read_lock();
10741086
return l ? pppoe_get_idx(pn, --l) : SEQ_START_TOKEN;
10751087
}
10761088

10771089
static void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos)
10781090
{
10791091
struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
1080-
struct pppox_sock *po;
1092+
struct pppox_sock *po, *next;
10811093

10821094
++*pos;
10831095
if (v == SEQ_START_TOKEN) {
10841096
po = pppoe_get_idx(pn, 0);
10851097
goto out;
10861098
}
10871099
po = v;
1088-
if (po->next)
1089-
po = po->next;
1100+
next = rcu_dereference(po->next);
1101+
if (next)
1102+
po = next;
10901103
else {
10911104
int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
10921105

10931106
po = NULL;
10941107
while (++hash < PPPOE_HASH_SIZE) {
1095-
po = pn->hash_table[hash];
1108+
po = rcu_dereference(pn->hash_table[hash]);
10961109
if (po)
10971110
break;
10981111
}
@@ -1103,10 +1116,9 @@ static void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos)
11031116
}
11041117

11051118
static void pppoe_seq_stop(struct seq_file *seq, void *v)
1106-
__releases(pn->hash_lock)
1119+
__releases(RCU)
11071120
{
1108-
struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
1109-
read_unlock_bh(&pn->hash_lock);
1121+
rcu_read_unlock();
11101122
}
11111123

11121124
static const struct seq_operations pppoe_seq_ops = {
@@ -1149,7 +1161,7 @@ static __net_init int pppoe_init_net(struct net *net)
11491161
struct pppoe_net *pn = pppoe_pernet(net);
11501162
struct proc_dir_entry *pde;
11511163

1152-
rwlock_init(&pn->hash_lock);
1164+
spin_lock_init(&pn->hash_lock);
11531165

11541166
pde = proc_create_net("pppoe", 0444, net->proc_net,
11551167
&pppoe_seq_ops, sizeof(struct seq_net_private));

include/linux/if_pppox.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ struct pppox_sock {
4343
/* struct sock must be the first member of pppox_sock */
4444
struct sock sk;
4545
struct ppp_channel chan;
46-
struct pppox_sock *next; /* for hash table */
46+
struct pppox_sock __rcu *next; /* for hash table */
4747
union {
4848
struct pppoe_opt pppoe;
4949
struct pptp_opt pptp;

0 commit comments

Comments
 (0)