Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ppp: remove rwlock usage

In struct channel, the upl lock is implemented using rwlock_t,
protecting access to pch->ppp and pch->bridge.

As previously discussed on the list, using rwlock in the network fast
path is not recommended.
This patch replaces the rwlock with a spinlock for writers, and uses RCU
for readers.

- pch->ppp and pch->bridge are now declared as __rcu pointers.
- Readers use rcu_dereference_bh() under rcu_read_lock_bh().
- Writers use spin_lock() to update.

Signed-off-by: Qingfang Deng <dqfext@gmail.com>
Link: https://patch.msgid.link/20250822012548.6232-1-dqfext@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Qingfang Deng and committed by
Jakub Kicinski
b8844aab 60c481d4

+63 -57
+63 -57
drivers/net/ppp/ppp_generic.c
··· 179 179 struct ppp_channel *chan; /* public channel data structure */ 180 180 struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */ 181 181 spinlock_t downl; /* protects `chan', file.xq dequeue */ 182 - struct ppp *ppp; /* ppp unit we're connected to */ 182 + struct ppp __rcu *ppp; /* ppp unit we're connected to */ 183 183 struct net *chan_net; /* the net channel belongs to */ 184 184 netns_tracker ns_tracker; 185 185 struct list_head clist; /* link in list of channels per unit */ 186 - rwlock_t upl; /* protects `ppp' and 'bridge' */ 186 + spinlock_t upl; /* protects `ppp' and 'bridge' */ 187 187 struct channel __rcu *bridge; /* "bridged" ppp channel */ 188 188 #ifdef CONFIG_PPP_MULTILINK 189 189 u8 avail; /* flag used in multilink stuff */ ··· 645 645 */ 646 646 static int ppp_bridge_channels(struct channel *pch, struct channel *pchb) 647 647 { 648 - write_lock_bh(&pch->upl); 649 - if (pch->ppp || 648 + spin_lock(&pch->upl); 649 + if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || 650 650 rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) { 651 - write_unlock_bh(&pch->upl); 651 + spin_unlock(&pch->upl); 652 652 return -EALREADY; 653 653 } 654 654 refcount_inc(&pchb->file.refcnt); 655 655 rcu_assign_pointer(pch->bridge, pchb); 656 - write_unlock_bh(&pch->upl); 656 + spin_unlock(&pch->upl); 657 657 658 - write_lock_bh(&pchb->upl); 659 - if (pchb->ppp || 658 + spin_lock(&pchb->upl); 659 + if (rcu_dereference_protected(pchb->ppp, lockdep_is_held(&pchb->upl)) || 660 660 rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl))) { 661 - write_unlock_bh(&pchb->upl); 661 + spin_unlock(&pchb->upl); 662 662 goto err_unset; 663 663 } 664 664 refcount_inc(&pch->file.refcnt); 665 665 rcu_assign_pointer(pchb->bridge, pch); 666 - write_unlock_bh(&pchb->upl); 666 + spin_unlock(&pchb->upl); 667 667 668 668 return 0; 669 669 670 670 err_unset: 671 - write_lock_bh(&pch->upl); 671 + spin_lock(&pch->upl); 672 672 /* Re-read pch->bridge with upl held in case it was modified concurrently */ 673 673 pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); 674 674 RCU_INIT_POINTER(pch->bridge, NULL); 675 - write_unlock_bh(&pch->upl); 675 + spin_unlock(&pch->upl); 676 676 synchronize_rcu(); 677 677 678 678 if (pchb) ··· 686 686 { 687 687 struct channel *pchb, *pchbb; 688 688 689 - write_lock_bh(&pch->upl); 689 + spin_lock(&pch->upl); 690 690 pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); 691 691 if (!pchb) { 692 - write_unlock_bh(&pch->upl); 692 + spin_unlock(&pch->upl); 693 693 return -EINVAL; 694 694 } 695 695 RCU_INIT_POINTER(pch->bridge, NULL); 696 - write_unlock_bh(&pch->upl); 696 + spin_unlock(&pch->upl); 697 697 698 698 /* Only modify pchb if phcb->bridge points back to pch. 699 699 * If not, it implies that there has been a race unbridging (and possibly 700 700 * even rebridging) pchb. We should leave pchb alone to avoid either a 701 701 * refcount underflow, or breaking another established bridge instance. 702 702 */ 703 - write_lock_bh(&pchb->upl); 703 + spin_lock(&pchb->upl); 704 704 pchbb = rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl)); 705 705 if (pchbb == pch) 706 706 RCU_INIT_POINTER(pchb->bridge, NULL); 707 - write_unlock_bh(&pchb->upl); 707 + spin_unlock(&pchb->upl); 708 708 709 709 synchronize_rcu(); 710 710 ··· 2158 2158 #endif /* CONFIG_PPP_MULTILINK */ 2159 2159 2160 2160 /* Try to send data out on a channel */ 2161 - static void __ppp_channel_push(struct channel *pch) 2161 + static void __ppp_channel_push(struct channel *pch, struct ppp *ppp) 2162 2162 { 2163 2163 struct sk_buff *skb; 2164 - struct ppp *ppp; 2165 2164 2166 2165 spin_lock(&pch->downl); 2167 2166 if (pch->chan) { ··· 2179 2180 spin_unlock(&pch->downl); 2180 2181 /* see if there is anything from the attached unit to be sent */ 2181 2182 if (skb_queue_empty(&pch->file.xq)) { 2182 - ppp = pch->ppp; 2183 2183 if (ppp) 2184 2184 __ppp_xmit_process(ppp, NULL); 2185 2185 } ··· 2187 2189 static void ppp_channel_push(struct channel *pch) 2188 2190 { 2189 2191 struct ppp_xmit_recursion *xmit_recursion; 2192 + struct ppp *ppp; 2190 2193 2191 - read_lock_bh(&pch->upl); 2192 - if (pch->ppp) { 2193 - xmit_recursion = this_cpu_ptr(pch->ppp->xmit_recursion); 2194 - local_lock_nested_bh(&pch->ppp->xmit_recursion->bh_lock); 2194 + rcu_read_lock_bh(); 2195 + ppp = rcu_dereference_bh(pch->ppp); 2196 + if (ppp) { 2197 + xmit_recursion = this_cpu_ptr(ppp->xmit_recursion); 2198 + local_lock_nested_bh(&ppp->xmit_recursion->bh_lock); 2195 2199 xmit_recursion->owner = current; 2196 - __ppp_channel_push(pch); 2200 + __ppp_channel_push(pch, ppp); 2197 2201 xmit_recursion->owner = NULL; 2198 - local_unlock_nested_bh(&pch->ppp->xmit_recursion->bh_lock); 2202 + local_unlock_nested_bh(&ppp->xmit_recursion->bh_lock); 2199 2203 } else { 2200 - __ppp_channel_push(pch); 2204 + __ppp_channel_push(pch, NULL); 2201 2205 } 2202 - read_unlock_bh(&pch->upl); 2206 + rcu_read_unlock_bh(); 2203 2207 } 2204 2208 2205 2209 /* ··· 2303 2303 ppp_input(struct ppp_channel *chan, struct sk_buff *skb) 2304 2304 { 2305 2305 struct channel *pch = chan->ppp; 2306 + struct ppp *ppp; 2306 2307 int proto; 2307 2308 2308 2309 if (!pch) { ··· 2315 2314 if (ppp_channel_bridge_input(pch, skb)) 2316 2315 return; 2317 2316 2318 - read_lock_bh(&pch->upl); 2317 + rcu_read_lock_bh(); 2318 + ppp = rcu_dereference_bh(pch->ppp); 2319 2319 if (!ppp_decompress_proto(skb)) { 2320 2320 kfree_skb(skb); 2321 - if (pch->ppp) { 2322 - ++pch->ppp->dev->stats.rx_length_errors; 2323 - ppp_receive_error(pch->ppp); 2321 + if (ppp) { 2322 + ++ppp->dev->stats.rx_length_errors; 2323 + ppp_receive_error(ppp); 2324 2324 } 2325 2325 goto done; 2326 2326 } 2327 2327 2328 2328 proto = PPP_PROTO(skb); 2329 - if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { 2329 + if (!ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { 2330 2330 /* put it on the channel queue */ 2331 2331 skb_queue_tail(&pch->file.rq, skb); 2332 2332 /* drop old frames if queue too long */ ··· 2336 2334 kfree_skb(skb); 2337 2335 wake_up_interruptible(&pch->file.rwait); 2338 2336 } else { 2339 - ppp_do_recv(pch->ppp, skb, pch); 2337 + ppp_do_recv(ppp, skb, pch); 2340 2338 } 2341 2339 2342 2340 done: 2343 - read_unlock_bh(&pch->upl); 2341 + rcu_read_unlock_bh(); 2344 2342 } 2345 2343 2346 2344 /* Put a 0-length skb in the receive queue as an error indication */ ··· 2349 2347 { 2350 2348 struct channel *pch = chan->ppp; 2351 2349 struct sk_buff *skb; 2350 + struct ppp *ppp; 2352 2351 2353 2352 if (!pch) 2354 2353 return; 2355 2354 2356 - read_lock_bh(&pch->upl); 2357 - if (pch->ppp) { 2355 + rcu_read_lock_bh(); 2356 + ppp = rcu_dereference_bh(pch->ppp); 2357 + if (ppp) { 2358 2358 skb = alloc_skb(0, GFP_ATOMIC); 2359 2359 if (skb) { 2360 2360 skb->len = 0; /* probably unnecessary */ 2361 2361 skb->cb[0] = code; 2362 - ppp_do_recv(pch->ppp, skb, pch); 2362 + ppp_do_recv(ppp, skb, pch); 2363 2363 } 2364 2364 } 2365 - read_unlock_bh(&pch->upl); 2365 + rcu_read_unlock_bh(); 2366 2366 } 2367 2367 2368 2368 /* ··· 2912 2908 2913 2909 pn = ppp_pernet(net); 2914 2910 2915 - pch->ppp = NULL; 2916 2911 pch->chan = chan; 2917 2912 pch->chan_net = get_net_track(net, &pch->ns_tracker, GFP_KERNEL); 2918 2913 chan->ppp = pch; ··· 2922 2919 #endif /* CONFIG_PPP_MULTILINK */ 2923 2920 init_rwsem(&pch->chan_sem); 2924 2921 spin_lock_init(&pch->downl); 2925 - rwlock_init(&pch->upl); 2922 + spin_lock_init(&pch->upl); 2926 2923 2927 2924 spin_lock_bh(&pn->all_channels_lock); 2928 2925 pch->file.index = ++pn->last_channel_index; ··· 2951 2948 int ppp_unit_number(struct ppp_channel *chan) 2952 2949 { 2953 2950 struct channel *pch = chan->ppp; 2951 + struct ppp *ppp; 2954 2952 int unit = -1; 2955 2953 2956 2954 if (pch) { 2957 - read_lock_bh(&pch->upl); 2958 - if (pch->ppp) 2959 - unit = pch->ppp->file.index; 2960 - read_unlock_bh(&pch->upl); 2955 + rcu_read_lock(); 2956 + ppp = rcu_dereference(pch->ppp); 2957 + if (ppp) 2958 + unit = ppp->file.index; 2959 + rcu_read_unlock(); 2961 2960 } 2962 2961 return unit; 2963 2962 } ··· 2971 2966 { 2972 2967 struct channel *pch = chan->ppp; 2973 2968 char *name = NULL; 2969 + struct ppp *ppp; 2974 2970 2975 2971 if (pch) { 2976 - read_lock_bh(&pch->upl); 2977 - if (pch->ppp && pch->ppp->dev) 2978 - name = pch->ppp->dev->name; 2979 - read_unlock_bh(&pch->upl); 2972 + rcu_read_lock(); 2973 + ppp = rcu_dereference(pch->ppp); 2974 + if (ppp && ppp->dev) 2975 + name = ppp->dev->name; 2976 + rcu_read_unlock(); 2980 2977 } 2981 2978 return name; 2982 2979 } ··· 3501 3494 ppp = ppp_find_unit(pn, unit); 3502 3495 if (!ppp) 3503 3496 goto out; 3504 - write_lock_bh(&pch->upl); 3497 + spin_lock(&pch->upl); 3505 3498 ret = -EINVAL; 3506 - if (pch->ppp || 3499 + if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || 3507 3500 rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) 3508 3501 goto outl; 3509 3502 ··· 3528 3521 ppp->dev->hard_header_len = hdrlen; 3529 3522 list_add_tail_rcu(&pch->clist, &ppp->channels); 3530 3523 ++ppp->n_channels; 3531 - pch->ppp = ppp; 3524 + rcu_assign_pointer(pch->ppp, ppp); 3532 3525 refcount_inc(&ppp->file.refcnt); 3533 3526 ppp_unlock(ppp); 3534 3527 ret = 0; 3535 3528 3536 3529 outl: 3537 - write_unlock_bh(&pch->upl); 3530 + spin_unlock(&pch->upl); 3538 3531 out: 3539 3532 mutex_unlock(&pn->all_ppp_mutex); 3540 3533 return ret; ··· 3549 3542 struct ppp *ppp; 3550 3543 int err = -EINVAL; 3551 3544 3552 - write_lock_bh(&pch->upl); 3553 - ppp = pch->ppp; 3554 - pch->ppp = NULL; 3555 - write_unlock_bh(&pch->upl); 3545 + spin_lock(&pch->upl); 3546 + ppp = rcu_replace_pointer(pch->ppp, NULL, lockdep_is_held(&pch->upl)); 3547 + spin_unlock(&pch->upl); 3556 3548 if (ppp) { 3557 3549 /* remove it from the ppp unit's list */ 3558 3550 ppp_lock(ppp);