[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240927174206.602651-1-quic_mojha@quicinc.com>
Date: Fri, 27 Sep 2024 23:12:06 +0530
From: Mukesh Ojha <quic_mojha@...cinc.com>
To: Linus Walleij <linus.walleij@...aro.org>
CC: <linux-gpio@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
Mukesh Ojha
<quic_mojha@...cinc.com>
Subject: [PATCH v2] pinmux: Use sequential access to access desc->pinmux data
When two client of the same gpio call pinctrl_select_state() for the
same functionality, we are seeing NULL pointer issue while accessing
desc->mux_owner.
Let's say two processes A, B executing in pin_request() for the same pin
and process A updates the desc->mux_usecount but not yet updated the
desc->mux_owner while process B see the desc->mux_usecount which got
updated by A path and further executes strcmp and while accessing
desc->mux_owner it crashes with NULL pointer.
Serialize the access to mux related setting with a spin lock.
cpu0 (process A) cpu1(process B)
pinctrl_select_state() { pinctrl_select_state() {
pin_request() { pin_request() {
...
....
} else {
desc->mux_usecount++;
desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
if (desc->mux_usecount > 1)
return 0;
desc->mux_owner = owner;
} }
Signed-off-by: Mukesh Ojha <quic_mojha@...cinc.com>
---
Changes in v2:
- Used scoped_guard and renamed lock name as per suggestion from Linus.W .
drivers/pinctrl/core.c | 3 +
drivers/pinctrl/core.h | 2 +
drivers/pinctrl/pinmux.c | 150 +++++++++++++++++++++------------------
3 files changed, 86 insertions(+), 69 deletions(-)
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 4061890a1748..b00911421cf5 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -220,6 +220,9 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,
/* Set owner */
pindesc->pctldev = pctldev;
+#ifdef CONFIG_PINMUX
+ spin_lock_init(&pindesc->mux_lock);
+#endif
/* Copy basic pin info */
if (pin->name) {
diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h
index 4e07707d2435..179e01dfacc2 100644
--- a/drivers/pinctrl/core.h
+++ b/drivers/pinctrl/core.h
@@ -12,6 +12,7 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/radix-tree.h>
+#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/pinctrl/machine.h>
@@ -177,6 +178,7 @@ struct pin_desc {
const char *mux_owner;
const struct pinctrl_setting_mux *mux_setting;
const char *gpio_owner;
+ spinlock_t mux_lock;
#endif
};
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 02033ea1c643..e4d535aabbb6 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -14,6 +14,7 @@
#include <linux/array_size.h>
#include <linux/ctype.h>
+#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -127,29 +128,31 @@ static int pin_request(struct pinctrl_dev *pctldev,
dev_dbg(pctldev->dev, "request pin %d (%s) for %s\n",
pin, desc->name, owner);
- if ((!gpio_range || ops->strict) &&
- desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
- dev_err(pctldev->dev,
- "pin %s already requested by %s; cannot claim for %s\n",
- desc->name, desc->mux_owner, owner);
- goto out;
- }
+ scoped_guard(spinlock_irqsave, &desc->mux_lock) {
+ if ((!gpio_range || ops->strict) &&
+ desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
+ dev_err(pctldev->dev,
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->mux_owner, owner);
+ goto out;
+ }
- if ((gpio_range || ops->strict) && desc->gpio_owner) {
- dev_err(pctldev->dev,
- "pin %s already requested by %s; cannot claim for %s\n",
- desc->name, desc->gpio_owner, owner);
- goto out;
- }
+ if ((gpio_range || ops->strict) && desc->gpio_owner) {
+ dev_err(pctldev->dev,
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->gpio_owner, owner);
+ goto out;
+ }
- if (gpio_range) {
- desc->gpio_owner = owner;
- } else {
- desc->mux_usecount++;
- if (desc->mux_usecount > 1)
- return 0;
+ if (gpio_range) {
+ desc->gpio_owner = owner;
+ } else {
+ desc->mux_usecount++;
+ if (desc->mux_usecount > 1)
+ return 0;
- desc->mux_owner = owner;
+ desc->mux_owner = owner;
+ }
}
/* Let each pin increase references to this module */
@@ -178,12 +181,14 @@ static int pin_request(struct pinctrl_dev *pctldev,
out_free_pin:
if (status) {
- if (gpio_range) {
- desc->gpio_owner = NULL;
- } else {
- desc->mux_usecount--;
- if (!desc->mux_usecount)
- desc->mux_owner = NULL;
+ scoped_guard(spinlock_irqsave, &desc->mux_lock) {
+ if (gpio_range) {
+ desc->gpio_owner = NULL;
+ } else {
+ desc->mux_usecount--;
+ if (!desc->mux_usecount)
+ desc->mux_owner = NULL;
+ }
}
}
out:
@@ -223,11 +228,13 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
/*
* A pin should not be freed more times than allocated.
*/
- if (WARN_ON(!desc->mux_usecount))
- return NULL;
- desc->mux_usecount--;
- if (desc->mux_usecount)
- return NULL;
+ scoped_guard(spinlock_irqsave, &desc->mux_lock) {
+ if (WARN_ON(!desc->mux_usecount))
+ return NULL;
+ desc->mux_usecount--;
+ if (desc->mux_usecount)
+ return NULL;
+ }
}
/*
@@ -239,13 +246,15 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
else if (ops->free)
ops->free(pctldev, pin);
- if (gpio_range) {
- owner = desc->gpio_owner;
- desc->gpio_owner = NULL;
- } else {
- owner = desc->mux_owner;
- desc->mux_owner = NULL;
- desc->mux_setting = NULL;
+ scoped_guard(spinlock_irqsave, &desc->mux_lock) {
+ if (gpio_range) {
+ owner = desc->gpio_owner;
+ desc->gpio_owner = NULL;
+ } else {
+ owner = desc->mux_owner;
+ desc->mux_owner = NULL;
+ desc->mux_setting = NULL;
+ }
}
module_put(pctldev->owner);
@@ -608,40 +617,43 @@ static int pinmux_pins_show(struct seq_file *s, void *what)
if (desc == NULL)
continue;
- if (desc->mux_owner &&
- !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev)))
- is_hog = true;
-
- if (pmxops->strict) {
- if (desc->mux_owner)
- seq_printf(s, "pin %d (%s): device %s%s",
- pin, desc->name, desc->mux_owner,
+ scoped_guard(spinlock_irqsave, &desc->mux_lock) {
+ if (desc->mux_owner &&
+ !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev)))
+ is_hog = true;
+
+ if (pmxops->strict) {
+ if (desc->mux_owner)
+ seq_printf(s, "pin %d (%s): device %s%s",
+ pin, desc->name, desc->mux_owner,
+ is_hog ? " (HOG)" : "");
+ else if (desc->gpio_owner)
+ seq_printf(s, "pin %d (%s): GPIO %s",
+ pin, desc->name, desc->gpio_owner);
+ else
+ seq_printf(s, "pin %d (%s): UNCLAIMED",
+ pin, desc->name);
+ } else {
+ /* For non-strict controllers */
+ seq_printf(s, "pin %d (%s): %s %s%s", pin, desc->name,
+ desc->mux_owner ? desc->mux_owner
+ : "(MUX UNCLAIMED)",
+ desc->gpio_owner ? desc->gpio_owner
+ : "(GPIO UNCLAIMED)",
is_hog ? " (HOG)" : "");
- else if (desc->gpio_owner)
- seq_printf(s, "pin %d (%s): GPIO %s",
- pin, desc->name, desc->gpio_owner);
+ }
+
+ /* If mux: print function+group claiming the pin */
+ if (desc->mux_setting)
+ seq_printf(s, " function %s group %s\n",
+ pmxops->get_function_name(pctldev,
+ desc->mux_setting->func),
+ pctlops->get_group_name(pctldev,
+ desc->mux_setting->group));
else
- seq_printf(s, "pin %d (%s): UNCLAIMED",
- pin, desc->name);
- } else {
- /* For non-strict controllers */
- seq_printf(s, "pin %d (%s): %s %s%s", pin, desc->name,
- desc->mux_owner ? desc->mux_owner
- : "(MUX UNCLAIMED)",
- desc->gpio_owner ? desc->gpio_owner
- : "(GPIO UNCLAIMED)",
- is_hog ? " (HOG)" : "");
- }
+ seq_putc(s, '\n');
- /* If mux: print function+group claiming the pin */
- if (desc->mux_setting)
- seq_printf(s, " function %s group %s\n",
- pmxops->get_function_name(pctldev,
- desc->mux_setting->func),
- pctlops->get_group_name(pctldev,
- desc->mux_setting->group));
- else
- seq_putc(s, '\n');
+ }
}
mutex_unlock(&pctldev->mutex);
--
2.34.1
Powered by blists - more mailing lists