[PATCH] wifi: mt76: mt7996: use spin_lock_irqsave for reg_lock
Valera Kozlov <[email protected]>
| Newsgroups | org.kernel.vger.linux-wireless |
|---|---|
| Message-ID | <[email protected]> |
mt7996_rr()/wr()/rmw() and mt7996_memcpy_fromio() take dev->reg_lock via spin_lock_bh() when accessing remapped registers. This is not safe against hard-IRQ context, which can lead to race conditions and kernel panics on some platforms. MediaTek's own vendor tree independently found and fixed this same issue, reporting it as causing AP panics via Asynchronous SError Interrupt during normal WiFi operation: https://github.com/mediatek/mtk-openwrt-feeds/commit/3edfc0cbf7dc666a0b051b54f51634d6ff2a3be6 Switch to spin_lock_irqsave()/spin_unlock_irqrestore() to make the locking safe against hard-IRQ context. Signed-off-by: Valera Kozlov <[email protected]> --- mt7996/mmio.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/mt7996/mmio.c b/mt7996/mmio.c index 1f7355cb..67ed157b 100644 --- a/mt7996/mmio.c +++ b/mt7996/mmio.c @@ -365,29 +365,31 @@ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset, size_t len) { u32 addr = __mt7996_reg_addr(dev, offset); + unsigned long flags; if (addr) { memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); return; } - spin_lock_bh(&dev->reg_lock); + spin_lock_irqsave(&dev->reg_lock, flags); memcpy_fromio(buf, dev->mt76.mmio.regs + __mt7996_reg_remap_addr(dev, offset), len); - spin_unlock_bh(&dev->reg_lock); + spin_unlock_irqrestore(&dev->reg_lock, flags); } static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset), val; + unsigned long flags; if (addr) return dev->bus_ops->rr(mdev, addr); - spin_lock_bh(&dev->reg_lock); + spin_lock_irqsave(&dev->reg_lock, flags); val = dev->bus_ops->rr(mdev, __mt7996_reg_remap_addr(dev, offset)); - spin_unlock_bh(&dev->reg_lock); + spin_unlock_irqrestore(&dev->reg_lock, flags); return val; } @@ -396,28 +398,30 @@ static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset); + unsigned long flags; if (addr) { dev->bus_ops->wr(mdev, addr, val); return; } - spin_lock_bh(&dev->reg_lock); + spin_lock_irqsave(&dev->reg_lock, flags); dev->bus_ops->wr(mdev, __mt7996_reg_remap_addr(dev, offset), val); - spin_unlock_bh(&dev->reg_lock); + spin_unlock_irqrestore(&dev->reg_lock, flags); } static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset); + unsigned long flags; if (addr) return dev->bus_ops->rmw(mdev, addr, mask, val); - spin_lock_bh(&dev->reg_lock); + spin_lock_irqsave(&dev->reg_lock, flags); val = dev->bus_ops->rmw(mdev, __mt7996_reg_remap_addr(dev, offset), mask, val); - spin_unlock_bh(&dev->reg_lock); + spin_unlock_irqrestore(&dev->reg_lock, flags); return val; } -- 2.53.0