From 257c25598584f9f7aaccbcd7b9a6faf23aec3156 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Fri, 7 Jun 2019 10:42:35 -0600 Subject: net: sfp: Stop SFP polling and interrupt handling during shutdown SFP device polling can cause problems during the shutdown process if the parent devices of the network controller have been shut down already. This problem was seen on the iMX6 platform with PCIe devices, where accessing the device after the bus is shut down causes a hang. Free any acquired GPIO interrupts and stop all delayed work in the SFP driver during the shutdown process, so that we ensure that no pending operations are still occurring after the SFP shutdown completes. Signed-off-by: Robert Hancock Signed-off-by: David S. Miller --- drivers/net/phy/sfp.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index a991c80e6567..44d787d36342 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -185,6 +185,7 @@ struct sfp { int (*write)(struct sfp *, bool, u8, void *, size_t); struct gpio_desc *gpio[GPIO_MAX]; + int gpio_irq[GPIO_MAX]; bool attached; unsigned int state; @@ -1802,7 +1803,7 @@ static int sfp_probe(struct platform_device *pdev) struct i2c_adapter *i2c; struct sfp *sfp; bool poll = false; - int irq, err, i; + int err, i; sfp = sfp_alloc(&pdev->dev); if (IS_ERR(sfp)) @@ -1901,19 +1902,22 @@ static int sfp_probe(struct platform_device *pdev) if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) continue; - irq = gpiod_to_irq(sfp->gpio[i]); - if (!irq) { + sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]); + if (!sfp->gpio_irq[i]) { poll = true; continue; } - err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq, + err = devm_request_threaded_irq(sfp->dev, sfp->gpio_irq[i], + NULL, sfp_irq, IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, dev_name(sfp->dev), sfp); - if (err) + if (err) { + sfp->gpio_irq[i] = 0; poll = true; + } } if (poll) @@ -1944,9 +1948,26 @@ static int sfp_remove(struct platform_device *pdev) return 0; } +static void sfp_shutdown(struct platform_device *pdev) +{ + struct sfp *sfp = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < GPIO_MAX; i++) { + if (!sfp->gpio_irq[i]) + continue; + + devm_free_irq(sfp->dev, sfp->gpio_irq[i], sfp); + } + + cancel_delayed_work_sync(&sfp->poll); + cancel_delayed_work_sync(&sfp->timeout); +} + static struct platform_driver sfp_driver = { .probe = sfp_probe, .remove = sfp_remove, + .shutdown = sfp_shutdown, .driver = { .name = "sfp", .of_match_table = sfp_of_match, -- cgit v1.2.3 From 2158e856f56bb762ef90f3ec244d41a519826f75 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Fri, 7 Jun 2019 10:42:36 -0600 Subject: net: sfp: add mutex to prevent concurrent state checks sfp_check_state can potentially be called by both a threaded IRQ handler and delayed work. If it is concurrently called, it could result in incorrect state management. Add a st_mutex to protect the state - this lock gets taken outside of code that checks and handle state changes, and the existing sm_mutex nests inside of it. Suggested-by: Russell King Signed-off-by: Robert Hancock Signed-off-by: David S. Miller --- drivers/net/phy/sfp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 44d787d36342..97960d73b4ab 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -188,10 +188,11 @@ struct sfp { int gpio_irq[GPIO_MAX]; bool attached; + struct mutex st_mutex; /* Protects state */ unsigned int state; struct delayed_work poll; struct delayed_work timeout; - struct mutex sm_mutex; + struct mutex sm_mutex; /* Protects state machine */ unsigned char sm_mod_state; unsigned char sm_dev_state; unsigned short sm_state; @@ -1721,6 +1722,7 @@ static void sfp_check_state(struct sfp *sfp) { unsigned int state, i, changed; + mutex_lock(&sfp->st_mutex); state = sfp_get_state(sfp); changed = state ^ sfp->state; changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT; @@ -1746,6 +1748,7 @@ static void sfp_check_state(struct sfp *sfp) sfp_sm_event(sfp, state & SFP_F_LOS ? SFP_E_LOS_HIGH : SFP_E_LOS_LOW); rtnl_unlock(); + mutex_unlock(&sfp->st_mutex); } static irqreturn_t sfp_irq(int irq, void *data) @@ -1776,6 +1779,7 @@ static struct sfp *sfp_alloc(struct device *dev) sfp->dev = dev; mutex_init(&sfp->sm_mutex); + mutex_init(&sfp->st_mutex); INIT_DELAYED_WORK(&sfp->poll, sfp_poll); INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout); -- cgit v1.2.3