aboutsummaryrefslogtreecommitdiffstats
path: root/lib/logic_pio.c
diff options
context:
space:
mode:
authorJohn Garry <john.garry@huawei.com>2019-07-30 09:29:52 -0400
committerWei Xu <xuwei5@hisilicon.com>2019-08-13 02:48:54 -0400
commit06709e81c668f5f56c65b806895b278517bd44e0 (patch)
tree0e75ab1e01c0636634df8bcfcc062d55eee73ac4 /lib/logic_pio.c
parent5f9e832c137075045d15cd6899ab0505cfb2ca4b (diff)
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: 031e3601869c ("lib: Add generic PIO mapping method") Signed-off-by: John Garry <john.garry@huawei.com> Signed-off-by: Wei Xu <xuwei5@hisilicon.com>
Diffstat (limited to 'lib/logic_pio.c')
-rw-r--r--lib/logic_pio.c49
1 files 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)
46 end = new_range->hw_start + new_range->size; 46 end = new_range->hw_start + new_range->size;
47 47
48 mutex_lock(&io_range_mutex); 48 mutex_lock(&io_range_mutex);
49 list_for_each_entry_rcu(range, &io_range_list, list) { 49 list_for_each_entry(range, &io_range_list, list) {
50 if (range->fwnode == new_range->fwnode) { 50 if (range->fwnode == new_range->fwnode) {
51 /* range already there */ 51 /* range already there */
52 goto end_register; 52 goto end_register;
@@ -108,26 +108,38 @@ end_register:
108 */ 108 */
109struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode) 109struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
110{ 110{
111 struct logic_pio_hwaddr *range; 111 struct logic_pio_hwaddr *range, *found_range = NULL;
112 112
113 rcu_read_lock();
113 list_for_each_entry_rcu(range, &io_range_list, list) { 114 list_for_each_entry_rcu(range, &io_range_list, list) {
114 if (range->fwnode == fwnode) 115 if (range->fwnode == fwnode) {
115 return range; 116 found_range = range;
117 break;
118 }
116 } 119 }
117 return NULL; 120 rcu_read_unlock();
121
122 return found_range;
118} 123}
119 124
120/* Return a registered range given an input PIO token */ 125/* Return a registered range given an input PIO token */
121static struct logic_pio_hwaddr *find_io_range(unsigned long pio) 126static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
122{ 127{
123 struct logic_pio_hwaddr *range; 128 struct logic_pio_hwaddr *range, *found_range = NULL;
124 129
130 rcu_read_lock();
125 list_for_each_entry_rcu(range, &io_range_list, list) { 131 list_for_each_entry_rcu(range, &io_range_list, list) {
126 if (in_range(pio, range->io_start, range->size)) 132 if (in_range(pio, range->io_start, range->size)) {
127 return range; 133 found_range = range;
134 break;
135 }
128 } 136 }
129 pr_err("PIO entry token %lx invalid\n", pio); 137 rcu_read_unlock();
130 return NULL; 138
139 if (!found_range)
140 pr_err("PIO entry token 0x%lx invalid\n", pio);
141
142 return found_range;
131} 143}
132 144
133/** 145/**
@@ -180,14 +192,23 @@ unsigned long logic_pio_trans_cpuaddr(resource_size_t addr)
180{ 192{
181 struct logic_pio_hwaddr *range; 193 struct logic_pio_hwaddr *range;
182 194
195 rcu_read_lock();
183 list_for_each_entry_rcu(range, &io_range_list, list) { 196 list_for_each_entry_rcu(range, &io_range_list, list) {
184 if (range->flags != LOGIC_PIO_CPU_MMIO) 197 if (range->flags != LOGIC_PIO_CPU_MMIO)
185 continue; 198 continue;
186 if (in_range(addr, range->hw_start, range->size)) 199 if (in_range(addr, range->hw_start, range->size)) {
187 return addr - range->hw_start + range->io_start; 200 unsigned long cpuaddr;
201
202 cpuaddr = addr - range->hw_start + range->io_start;
203
204 rcu_read_unlock();
205 return cpuaddr;
206 }
188 } 207 }
189 pr_err("addr %llx not registered in io_range_list\n", 208 rcu_read_unlock();
190 (unsigned long long) addr); 209
210 pr_err("addr %pa not registered in io_range_list\n", &addr);
211
191 return ~0UL; 212 return ~0UL;
192} 213}
193 214