Commit 3429fb3c authored by Jon Hunter's avatar Jon Hunter Committed by Linus Walleij

pinctrl: Fix panic when pinctrl devices with hogs are unregistered

Commit df61b366af26 ('pinctrl: core: Use delayed work for hogs')
deferred part of the registration for pinctrl devices if the pinctrl
device has hogs. This introduced a window where if the pinctrl device
with hogs was sucessfully registered, but then unregistered again
(which could be caused by parent device being probe deferred) before
the delayed work has chanced to run, then this will cause a kernel
panic to occur because:

1. The 'pctldev->p' has not yet been initialised and when unregistering
   the pinctrl device we only check to see if it is an error value, but
   now it could also be NULL.
2. The pinctrl device may not have been added to the 'pinctrldev_list'
   list and we don't check to see if it was added before removing.

Fix up the above by checking to see if the 'pctldev->p' pointer is an
error value or NULL before putting the pinctrl device and verifying
that the pinctrl device is present in 'pinctrldev_list' before removing.

Fixes: df61b366af26 ('pinctrl: core: Use delayed work for hogs')
Signed-off-by: default avatarJon Hunter <jonathanh@nvidia.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 6ac4c1ad
...@@ -2064,6 +2064,8 @@ EXPORT_SYMBOL_GPL(pinctrl_register); ...@@ -2064,6 +2064,8 @@ EXPORT_SYMBOL_GPL(pinctrl_register);
void pinctrl_unregister(struct pinctrl_dev *pctldev) void pinctrl_unregister(struct pinctrl_dev *pctldev)
{ {
struct pinctrl_gpio_range *range, *n; struct pinctrl_gpio_range *range, *n;
struct pinctrl_dev *p, *p1;
if (pctldev == NULL) if (pctldev == NULL)
return; return;
...@@ -2072,13 +2074,15 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) ...@@ -2072,13 +2074,15 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev)
pinctrl_remove_device_debugfs(pctldev); pinctrl_remove_device_debugfs(pctldev);
mutex_unlock(&pctldev->mutex); mutex_unlock(&pctldev->mutex);
if (!IS_ERR(pctldev->p)) if (!IS_ERR_OR_NULL(pctldev->p))
pinctrl_put(pctldev->p); pinctrl_put(pctldev->p);
mutex_lock(&pinctrldev_list_mutex); mutex_lock(&pinctrldev_list_mutex);
mutex_lock(&pctldev->mutex); mutex_lock(&pctldev->mutex);
/* TODO: check that no pinmuxes are still active? */ /* TODO: check that no pinmuxes are still active? */
list_del(&pctldev->node); list_for_each_entry_safe(p, p1, &pinctrldev_list, node)
if (p == pctldev)
list_del(&p->node);
pinmux_generic_free_functions(pctldev); pinmux_generic_free_functions(pctldev);
pinctrl_generic_free_groups(pctldev); pinctrl_generic_free_groups(pctldev);
/* Destroy descriptor tree */ /* Destroy descriptor tree */
......
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