diff options
author | John Garry <john.garry@huawei.com> | 2019-07-30 09:29:52 -0400 |
---|---|---|
committer | Wei Xu <xuwei5@hisilicon.com> | 2019-08-13 02:48:54 -0400 |
commit | 06709e81c668f5f56c65b806895b278517bd44e0 (patch) | |
tree | 0e75ab1e01c0636634df8bcfcc062d55eee73ac4 /lib/logic_pio.c | |
parent | 5f9e832c137075045d15cd6899ab0505cfb2ca4b (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.c | 49 |
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 | */ |
109 | struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode) | 109 | struct 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 */ |
121 | static struct logic_pio_hwaddr *find_io_range(unsigned long pio) | 126 | static 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 | ||