1
0
Fork 0
alistair23-linux/drivers/net/ethernet/cisco/enic
Thomas Gleixner df63949a27 net: enic: Cure the enic api locking trainwreck
[ Upstream commit a53b59ece8 ]

enic_dev_wait() has a BUG_ON(in_interrupt()).

Chasing the callers of enic_dev_wait() revealed the gems of enic_reset()
and enic_tx_hang_reset() which are both invoked through work queues in
order to be able to call rtnl_lock(). So far so good.

After locking rtnl both functions acquire enic::enic_api_lock which
serializes against the (ab)use from infiniband. This is where the
trainwreck starts.

enic::enic_api_lock is a spin_lock() which implicitly disables preemption,
but both functions invoke a ton of functions under that lock which can
sleep. The BUG_ON(in_interrupt()) does not trigger in that case because it
can't detect the preempt disabled condition.

This clearly has never been tested with any of the mandatory debug options
for 7+ years, which would have caught that for sure.

Cure it by adding a enic_api_busy member to struct enic, which is modified
and evaluated with enic::enic_api_lock held.

If enic_api_devcmd_proxy_by_index() observes enic::enic_api_busy as true,
it drops enic::enic_api_lock and busy waits for enic::enic_api_busy to
become false.

It would be smarter to wait for a completion of that busy period, but
enic_api_devcmd_proxy_by_index() is called with other spin locks held which
obviously can't sleep.

Remove the BUG_ON(in_interrupt()) check as well because it's incomplete and
with proper debugging enabled the problem would have been caught from the
debug checks in schedule_timeout().

Fixes: 0b038566c0 ("drivers/net: enic: Add an interface for USNIC to interact with firmware")
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2020-10-29 09:57:43 +01:00
..
Kconfig treewide: Add SPDX license identifier - Makefile/Kconfig 2019-05-21 10:50:46 +02:00
Makefile treewide: Add SPDX license identifier - Makefile/Kconfig 2019-05-21 10:50:46 +02:00
cq_desc.h
cq_enet_desc.h
enic.h net: enic: Cure the enic api locking trainwreck 2020-10-29 09:57:43 +01:00
enic_api.c net: enic: Cure the enic api locking trainwreck 2020-10-29 09:57:43 +01:00
enic_api.h
enic_clsf.c drivers: net: Remove unnecessary semicolon 2019-03-01 23:13:49 -08:00
enic_clsf.h treewide: setup_timer() -> timer_setup() (2 field) 2017-11-21 15:57:09 -08:00
enic_dev.c
enic_dev.h
enic_ethtool.c net: core: dev: Add extack argument to dev_open() 2018-12-06 13:26:06 -08:00
enic_main.c net: enic: Cure the enic api locking trainwreck 2020-10-29 09:57:43 +01:00
enic_pp.c
enic_pp.h
enic_res.c enic: fix UDP rss bits 2018-06-06 09:09:09 -04:00
enic_res.h
rq_enet_desc.h
vnic_cq.c
vnic_cq.h
vnic_dev.c net: cisco: enic: Replace GFP_ATOMIC with GFP_KERNEL 2018-08-04 13:08:06 -07:00
vnic_dev.h enic: fix UDP rss bits 2018-06-06 09:09:09 -04:00
vnic_devcmd.h enic: fix UDP rss bits 2018-06-06 09:09:09 -04:00
vnic_enet.h
vnic_intr.c
vnic_intr.h
vnic_nic.h enic: fix UDP rss bits 2018-06-06 09:09:09 -04:00
vnic_resource.h
vnic_rq.c net: cisco: enic: Replace GFP_ATOMIC with GFP_KERNEL 2018-08-04 13:08:06 -07:00
vnic_rq.h
vnic_rss.h
vnic_stats.h
vnic_vic.c
vnic_vic.h
vnic_wq.c net: cisco: enic: Replace GFP_ATOMIC with GFP_KERNEL 2018-08-04 13:08:06 -07:00
vnic_wq.h
wq_enet_desc.h