Re: [PATCH 1/5] lib: logic_pio: Fix RCU usage

From: Bjorn Helgaas
Date: Fri Jun 21 2019 - 09:43:38 EST


On Thu, Jun 20, 2019 at 06:31:52PM +0800, John Garry wrote:
> The traversing of io_range_list with list_for_each_entry_rcu()
> is not properly protected by rcu_read_lock(), so add it.
>
> In addition, the list traversing used in logic_pio_register_range()
> does not need to use the rcu variant.

Not being an RCU expert myself, a few words here about why one path
needs protection but the other doesn't would be helpful. This
basically restates what the patch *does*, which is obvious from the
diff, but not *why*.

> Fixes: 031e3601869c ("lib: Add generic PIO mapping method")
> Signed-off-by: John Garry <john.garry@xxxxxxxxxx>
> ---
> lib/logic_pio.c | 49 +++++++++++++++++++++++++++++++++++--------------
> 1 file changed, 35 insertions(+), 14 deletions(-)
>
> diff --git a/lib/logic_pio.c b/lib/logic_pio.c
> index feea48fd1a0d..761296376fbc 100644
> --- a/lib/logic_pio.c
> +++ b/lib/logic_pio.c
> @@ -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;
> }
>
> --
> 2.17.1
>