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

pppoatm: optimise PPP channel wakeups after sock_owned_by_user()

We don't need to schedule the wakeup tasklet on *every* unlock; only if we
actually blocked the channel in the first place.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Acked-by: Krzysztof Mazur <krzysiek@podlesie.net>

+19 -2
+19 -2
net/atm/pppoatm.c
··· 113 113 { 114 114 struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); 115 115 116 - tasklet_schedule(&pvcc->wakeup_tasklet); 116 + /* 117 + * As in pppoatm_pop(), it's safe to clear the BLOCKED bit here because 118 + * the wakeup *can't* race with pppoatm_send(). They both hold the PPP 119 + * channel's ->downl lock. And the potential race with *setting* it, 120 + * which leads to the double-check dance in pppoatm_may_send(), doesn't 121 + * exist here. In the sock_owned_by_user() case in pppoatm_send(), we 122 + * set the BLOCKED bit while the socket is still locked. We know that 123 + * ->release_cb() can't be called until that's done. 124 + */ 125 + if (test_and_clear_bit(BLOCKED, &pvcc->blocked)) 126 + tasklet_schedule(&pvcc->wakeup_tasklet); 117 127 if (pvcc->old_release_cb) 118 128 pvcc->old_release_cb(atmvcc); 119 129 } ··· 302 292 303 293 vcc = ATM_SKB(skb)->vcc; 304 294 bh_lock_sock(sk_atm(vcc)); 305 - if (sock_owned_by_user(sk_atm(vcc))) 295 + if (sock_owned_by_user(sk_atm(vcc))) { 296 + /* 297 + * Needs to happen (and be flushed, hence test_and_) before we unlock 298 + * the socket. It needs to be seen by the time our ->release_cb gets 299 + * called. 300 + */ 301 + test_and_set_bit(BLOCKED, &pvcc->blocked); 306 302 goto nospace; 303 + } 307 304 if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 308 305 test_bit(ATM_VF_CLOSE, &vcc->flags) || 309 306 !test_bit(ATM_VF_READY, &vcc->flags)) {