lib: logic_pio: Fix RCU usage
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: 031e360186
("lib: Add generic PIO mapping method")
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Wei Xu <xuwei5@hisilicon.com>
This commit is contained in:
parent
5f9e832c13
commit
06709e81c6
|
@ -46,7 +46,7 @@ int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
|
||||||
end = new_range->hw_start + new_range->size;
|
end = new_range->hw_start + new_range->size;
|
||||||
|
|
||||||
mutex_lock(&io_range_mutex);
|
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) {
|
if (range->fwnode == new_range->fwnode) {
|
||||||
/* range already there */
|
/* range already there */
|
||||||
goto end_register;
|
goto end_register;
|
||||||
|
@ -108,26 +108,38 @@ end_register:
|
||||||
*/
|
*/
|
||||||
struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
|
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) {
|
list_for_each_entry_rcu(range, &io_range_list, list) {
|
||||||
if (range->fwnode == fwnode)
|
if (range->fwnode == fwnode) {
|
||||||
return range;
|
found_range = range;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return found_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a registered range given an input PIO token */
|
/* Return a registered range given an input PIO token */
|
||||||
static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
|
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) {
|
list_for_each_entry_rcu(range, &io_range_list, list) {
|
||||||
if (in_range(pio, range->io_start, range->size))
|
if (in_range(pio, range->io_start, range->size)) {
|
||||||
return range;
|
found_range = range;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pr_err("PIO entry token %lx invalid\n", pio);
|
rcu_read_unlock();
|
||||||
return NULL;
|
|
||||||
|
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;
|
struct logic_pio_hwaddr *range;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
list_for_each_entry_rcu(range, &io_range_list, list) {
|
list_for_each_entry_rcu(range, &io_range_list, list) {
|
||||||
if (range->flags != LOGIC_PIO_CPU_MMIO)
|
if (range->flags != LOGIC_PIO_CPU_MMIO)
|
||||||
continue;
|
continue;
|
||||||
if (in_range(addr, range->hw_start, range->size))
|
if (in_range(addr, range->hw_start, range->size)) {
|
||||||
return addr - range->hw_start + range->io_start;
|
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",
|
rcu_read_unlock();
|
||||||
(unsigned long long) addr);
|
|
||||||
|
pr_err("addr %pa not registered in io_range_list\n", &addr);
|
||||||
|
|
||||||
return ~0UL;
|
return ~0UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue