Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b865c2c6 authored by John Garry's avatar John Garry Committed by Greg Kroah-Hartman
Browse files

lib: logic_pio: Fix RCU usage



commit 06709e81c668f5f56c65b806895b278517bd44e0 upstream.

The traversing of io_range_list with list_for_each_entry_rcu()
is not properly protected by rcu_read_lock() and rcu_read_unlock(),
so add them.

These functions mark the critical section scope where the list is
protected for the reader, it cannot be  "reclaimed". Any updater - in
this case, the logical PIO registration functions - cannot update the
list until the reader exits this critical section.

In addition, the list traversing used in logic_pio_register_range()
does not need to use the rcu variant.

This is because we are already using io_range_mutex to guarantee mutual
exclusion from mutating the list.

Cc: stable@vger.kernel.org
Fixes: 031e3601 ("lib: Add generic PIO mapping method")
Signed-off-by: default avatarJohn Garry <john.garry@huawei.com>
Signed-off-by: default avatarWei Xu <xuwei5@hisilicon.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 79829fc4
Loading
Loading
Loading
Loading
+35 −14
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
	end = new_range->hw_start + new_range->size;

	mutex_lock(&io_range_mutex);
	list_for_each_entry_rcu(range, &io_range_list, list) {
	list_for_each_entry(range, &io_range_list, list) {
		if (range->fwnode == new_range->fwnode) {
			/* range already there */
			goto end_register;
@@ -108,26 +108,38 @@ int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
 */
struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
{
	struct logic_pio_hwaddr *range;
	struct logic_pio_hwaddr *range, *found_range = NULL;

	rcu_read_lock();
	list_for_each_entry_rcu(range, &io_range_list, list) {
		if (range->fwnode == fwnode)
			return range;
		if (range->fwnode == fwnode) {
			found_range = range;
			break;
		}
	return NULL;
	}
	rcu_read_unlock();

	return found_range;
}

/* Return a registered range given an input PIO token */
static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
{
	struct logic_pio_hwaddr *range;
	struct logic_pio_hwaddr *range, *found_range = NULL;

	rcu_read_lock();
	list_for_each_entry_rcu(range, &io_range_list, list) {
		if (in_range(pio, range->io_start, range->size))
			return range;
		if (in_range(pio, range->io_start, range->size)) {
			found_range = range;
			break;
		}
	}
	pr_err("PIO entry token %lx invalid\n", pio);
	return NULL;
	rcu_read_unlock();

	if (!found_range)
		pr_err("PIO entry token 0x%lx invalid\n", pio);

	return found_range;
}

/**
@@ -180,14 +192,23 @@ unsigned long logic_pio_trans_cpuaddr(resource_size_t addr)
{
	struct logic_pio_hwaddr *range;

	rcu_read_lock();
	list_for_each_entry_rcu(range, &io_range_list, list) {
		if (range->flags != LOGIC_PIO_CPU_MMIO)
			continue;
		if (in_range(addr, range->hw_start, range->size))
			return addr - range->hw_start + range->io_start;
		if (in_range(addr, range->hw_start, range->size)) {
			unsigned long cpuaddr;

			cpuaddr = addr - range->hw_start + range->io_start;

			rcu_read_unlock();
			return cpuaddr;
		}
	pr_err("addr %llx not registered in io_range_list\n",
	       (unsigned long long) addr);
	}
	rcu_read_unlock();

	pr_err("addr %pa not registered in io_range_list\n", &addr);

	return ~0UL;
}