Commit 8d44b5cf authored by Leon Romanovsky's avatar Leon Romanovsky Committed by David S. Miller

liquidio: Overcome missing device lock protection in init/remove flows

The liquidio driver is broken by design. It initialize PCI devices
in separate delayed works. It causes to the situation where device lock
is dropped during initialize and remove sequences.

That lock is part of driver/core and needed to protect from races during
init, destroy and bus invocations.

In addition to lack of locking protection, it has incorrect order of
destroy flows and very questionable synchronization scheme based on
atomic_t.

This change doesn't fix that driver but makes sure that rest of the
netdev subsystem doesn't suffer from such basic protection by adding
device_lock over devlink_*() APIs and by moving devlink_register()
to be last command in setup_nic_devices().
Signed-off-by: default avatarLeon Romanovsky <leonro@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5df290e7
...@@ -1279,6 +1279,14 @@ static int liquidio_stop_nic_module(struct octeon_device *oct) ...@@ -1279,6 +1279,14 @@ static int liquidio_stop_nic_module(struct octeon_device *oct)
struct lio *lio; struct lio *lio;
dev_dbg(&oct->pci_dev->dev, "Stopping network interfaces\n"); dev_dbg(&oct->pci_dev->dev, "Stopping network interfaces\n");
device_lock(&oct->pci_dev->dev);
if (oct->devlink) {
devlink_unregister(oct->devlink);
devlink_free(oct->devlink);
oct->devlink = NULL;
}
device_unlock(&oct->pci_dev->dev);
if (!oct->ifcount) { if (!oct->ifcount) {
dev_err(&oct->pci_dev->dev, "Init for Octeon was not completed\n"); dev_err(&oct->pci_dev->dev, "Init for Octeon was not completed\n");
return 1; return 1;
...@@ -1300,12 +1308,6 @@ static int liquidio_stop_nic_module(struct octeon_device *oct) ...@@ -1300,12 +1308,6 @@ static int liquidio_stop_nic_module(struct octeon_device *oct)
for (i = 0; i < oct->ifcount; i++) for (i = 0; i < oct->ifcount; i++)
liquidio_destroy_nic_device(oct, i); liquidio_destroy_nic_device(oct, i);
if (oct->devlink) {
devlink_unregister(oct->devlink);
devlink_free(oct->devlink);
oct->devlink = NULL;
}
dev_dbg(&oct->pci_dev->dev, "Network interfaces stopped\n"); dev_dbg(&oct->pci_dev->dev, "Network interfaces stopped\n");
return 0; return 0;
} }
...@@ -3749,10 +3751,12 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) ...@@ -3749,10 +3751,12 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
} }
} }
device_lock(&octeon_dev->pci_dev->dev);
devlink = devlink_alloc(&liquidio_devlink_ops, devlink = devlink_alloc(&liquidio_devlink_ops,
sizeof(struct lio_devlink_priv), sizeof(struct lio_devlink_priv),
&octeon_dev->pci_dev->dev); &octeon_dev->pci_dev->dev);
if (!devlink) { if (!devlink) {
device_unlock(&octeon_dev->pci_dev->dev);
dev_err(&octeon_dev->pci_dev->dev, "devlink alloc failed\n"); dev_err(&octeon_dev->pci_dev->dev, "devlink alloc failed\n");
goto setup_nic_dev_free; goto setup_nic_dev_free;
} }
...@@ -3760,9 +3764,10 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) ...@@ -3760,9 +3764,10 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
lio_devlink = devlink_priv(devlink); lio_devlink = devlink_priv(devlink);
lio_devlink->oct = octeon_dev; lio_devlink->oct = octeon_dev;
devlink_register(devlink);
octeon_dev->devlink = devlink; octeon_dev->devlink = devlink;
octeon_dev->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; octeon_dev->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
devlink_register(devlink);
device_unlock(&octeon_dev->pci_dev->dev);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment