net: usbnet: fix potential deadlock on 32bit hosts
Marek reported a LOCKDEP issue occurring on 32bit host, that we tracked down to the fact that usbnet could either run from soft or hard irqs. This patch adds u64_stats_update_begin_irqsave() and u64_stats_update_end_irqrestore() helpers to solve this case. [ 17.768040] ================================ [ 17.772239] WARNING: inconsistent lock state [ 17.776511] 4.16.0-rc3-next-20180227-00007-g876c53a7493c #453 Not tainted [ 17.783329] -------------------------------- [ 17.787580] inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. [ 17.793607] swapper/0/0 [HC0[0]:SC1[1]:HE1:SE0] takes: [ 17.798751] (&syncp->seq#5){?.-.}, at: [<9b22e5f0>] asix_rx_fixup_internal+0x188/0x288 [ 17.806790] {IN-HARDIRQ-W} state was registered at: [ 17.811677] tx_complete+0x100/0x208 [ 17.815319] __usb_hcd_giveback_urb+0x60/0xf0 [ 17.819770] xhci_giveback_urb_in_irq+0xa8/0x240 [ 17.824469] xhci_td_cleanup+0xf4/0x16c [ 17.828367] xhci_irq+0xe74/0x2240 [ 17.831827] usb_hcd_irq+0x24/0x38 [ 17.835343] __handle_irq_event_percpu+0x98/0x510 [ 17.840111] handle_irq_event_percpu+0x1c/0x58 [ 17.844623] handle_irq_event+0x38/0x5c [ 17.848519] handle_fasteoi_irq+0xa4/0x138 [ 17.852681] generic_handle_irq+0x18/0x28 [ 17.856760] __handle_domain_irq+0x6c/0xe4 [ 17.860941] gic_handle_irq+0x54/0xa0 [ 17.864666] __irq_svc+0x70/0xb0 [ 17.867964] arch_cpu_idle+0x20/0x3c [ 17.871578] arch_cpu_idle+0x20/0x3c [ 17.875190] do_idle+0x144/0x218 [ 17.878468] cpu_startup_entry+0x18/0x1c [ 17.882454] start_kernel+0x394/0x400 [ 17.886177] irq event stamp: 161912 [ 17.889616] hardirqs last enabled at (161912): [<7bedfacf>] __netdev_alloc_skb+0xcc/0x140 [ 17.897893] hardirqs last disabled at (161911): [<d58261d0>] __netdev_alloc_skb+0x94/0x140 [ 17.904903] exynos5-hsi2c 12ca0000.i2c: tx timeout [ 17.906116] softirqs last enabled at (161904): [<387102ff>] irq_enter+0x78/0x80 [ 17.906123] softirqs last disabled at (161905): [<cf4c628e>] irq_exit+0x134/0x158 [ 17.925722]. [ 17.925722] other info that might help us debug this: [ 17.933435] Possible unsafe locking scenario: [ 17.933435]. [ 17.940331] CPU0 [ 17.942488] ---- [ 17.944894] lock(&syncp->seq#5); [ 17.948274] <Interrupt> [ 17.950847] lock(&syncp->seq#5); [ 17.954386]. [ 17.954386] *** DEADLOCK *** [ 17.954386]. [ 17.962422] no locks held by swapper/0/0. Fixes: c8b5d129ee29 ("net: usbnet: support 64bit stats") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Marek Szyprowski <m.szyprowski@samsung.com> Cc: Greg Ungerer <gerg@linux-m68k.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
35d889d10b
commit
2695578b89
@ -315,6 +315,7 @@ static void __usbnet_status_stop_force(struct usbnet *dev)
|
|||||||
void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
|
void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
||||||
|
unsigned long flags;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (test_bit(EVENT_RX_PAUSED, &dev->flags)) {
|
if (test_bit(EVENT_RX_PAUSED, &dev->flags)) {
|
||||||
@ -326,10 +327,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
|
|||||||
if (skb->protocol == 0)
|
if (skb->protocol == 0)
|
||||||
skb->protocol = eth_type_trans (skb, dev->net);
|
skb->protocol = eth_type_trans (skb, dev->net);
|
||||||
|
|
||||||
u64_stats_update_begin(&stats64->syncp);
|
flags = u64_stats_update_begin_irqsave(&stats64->syncp);
|
||||||
stats64->rx_packets++;
|
stats64->rx_packets++;
|
||||||
stats64->rx_bytes += skb->len;
|
stats64->rx_bytes += skb->len;
|
||||||
u64_stats_update_end(&stats64->syncp);
|
u64_stats_update_end_irqrestore(&stats64->syncp, flags);
|
||||||
|
|
||||||
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
|
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
|
||||||
skb->len + sizeof (struct ethhdr), skb->protocol);
|
skb->len + sizeof (struct ethhdr), skb->protocol);
|
||||||
@ -1248,11 +1249,12 @@ static void tx_complete (struct urb *urb)
|
|||||||
|
|
||||||
if (urb->status == 0) {
|
if (urb->status == 0) {
|
||||||
struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
u64_stats_update_begin(&stats64->syncp);
|
flags = u64_stats_update_begin_irqsave(&stats64->syncp);
|
||||||
stats64->tx_packets += entry->packets;
|
stats64->tx_packets += entry->packets;
|
||||||
stats64->tx_bytes += entry->length;
|
stats64->tx_bytes += entry->length;
|
||||||
u64_stats_update_end(&stats64->syncp);
|
u64_stats_update_end_irqrestore(&stats64->syncp, flags);
|
||||||
} else {
|
} else {
|
||||||
dev->net->stats.tx_errors++;
|
dev->net->stats.tx_errors++;
|
||||||
|
|
||||||
|
@ -90,6 +90,28 @@ static inline void u64_stats_update_end(struct u64_stats_sync *syncp)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
u64_stats_update_begin_irqsave(struct u64_stats_sync *syncp)
|
||||||
|
{
|
||||||
|
unsigned long flags = 0;
|
||||||
|
|
||||||
|
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
|
||||||
|
local_irq_save(flags);
|
||||||
|
write_seqcount_begin(&syncp->seq);
|
||||||
|
#endif
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
u64_stats_update_end_irqrestore(struct u64_stats_sync *syncp,
|
||||||
|
unsigned long flags)
|
||||||
|
{
|
||||||
|
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
|
||||||
|
write_seqcount_end(&syncp->seq);
|
||||||
|
local_irq_restore(flags);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static inline void u64_stats_update_begin_raw(struct u64_stats_sync *syncp)
|
static inline void u64_stats_update_begin_raw(struct u64_stats_sync *syncp)
|
||||||
{
|
{
|
||||||
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
|
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user