pinctrl: generic: serialise pinctrl_generic_dt_node_to_map()

pinctrl_generic_add_group() documents that the caller must take care of
locking, and pinmux_generic_add_function() needs it too, but
pinctrl_generic_dt_node_to_map() calls them without holding
pctldev->mutex, and the core caller in create_pinctrl() does not take it
either.

The driver core calls pinctrl_bind_pins() before probing a device, so
two devices that reference the same pin controller can run
pinctrl_generic_dt_node_to_map() on one pctldev at the same time.

Both `add` functions take the new selector from pctldev->num_groups or
pctldev->num_functions, and radix_tree_insert() at that index.
Two racing callers can read the same selector before either
has inserted, so the second insert collides and fails:

  k1-pinctrl d401e000.pinctrl:
  error -EEXIST: error adding function pcie2-0-cfg
  k1-pinctrl d401e000.pinctrl:
  does not have pin group pcie0-0-cfg.pcie0-0-pins

leaving one consumer without its pin configuration.

This was hit on a SpacemiT K3 board, where PCIe devices probe in parallel
against the single shared pin controller.

Take pctldev->mutex across the whole function, so that the groups and the
function referring are in a single critical section.

Fixes: 43722575e5 ("pinctrl: add generic functions + pins mapper")
Signed-off-by: Sarah Emery <sarah.emery@canonical.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
This commit is contained in:
Sarah Emery 2026-08-28 17:58:17 +02:00 committed by Linus Walleij
parent 692f32609a
commit 51ae996594

View File

@ -3,8 +3,10 @@
#define pr_fmt(fmt) "generic pinconfig core: " fmt
#include <linux/array_size.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/slab.h>
@ -196,6 +198,8 @@ static int pinctrl_generic_dt_node_to_map(struct pinctrl_dev *pctldev,
int ngroups = 0;
int ret;
guard(mutex)(&pctldev->mutex);
*maps = NULL;
*num_maps = 0;