diff options
author | Gavin Shan <shangw@linux.vnet.ibm.com> | 2012-09-07 18:44:08 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-09-09 19:35:31 -0400 |
commit | 22f4ab123f10e1862579785c73d9e60fa24afd4f (patch) | |
tree | b36110765abf6ef84fda7da26944c2ec6643a011 | |
parent | 55037d176107c33ac79528bf9ab282a6b0b51e16 (diff) |
powerpc/eeh: Search PE based on requirement
The patch implements searching PE based on the following
requirements:
* Search PE according to PE address, which is traditional
PE address that is composed of PCI bus/device/function
number, or unified PE address assigned by firmware or
platform.
* Search parent PE according to the given EEH device. It's
useful when creating new PE and put it into right position.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | arch/powerpc/include/asm/eeh.h | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_pe.c | 143 |
2 files changed, 144 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 7b9c7d65ee2d..1cc1388d6201 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h | |||
@@ -164,6 +164,7 @@ static inline void eeh_unlock(void) | |||
164 | */ | 164 | */ |
165 | #define EEH_MAX_ALLOWED_FREEZES 5 | 165 | #define EEH_MAX_ALLOWED_FREEZES 5 |
166 | 166 | ||
167 | typedef void *(*eeh_traverse_func)(void *data, void *flag); | ||
167 | int __devinit eeh_phb_pe_create(struct pci_controller *phb); | 168 | int __devinit eeh_phb_pe_create(struct pci_controller *phb); |
168 | 169 | ||
169 | void * __devinit eeh_dev_init(struct device_node *dn, void *data); | 170 | void * __devinit eeh_dev_init(struct device_node *dn, void *data); |
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c index 7dee7dc83522..f74b350c392d 100644 --- a/arch/powerpc/platforms/pseries/eeh_pe.c +++ b/arch/powerpc/platforms/pseries/eeh_pe.c | |||
@@ -118,3 +118,146 @@ static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb) | |||
118 | 118 | ||
119 | return NULL; | 119 | return NULL; |
120 | } | 120 | } |
121 | |||
122 | /** | ||
123 | * eeh_pe_next - Retrieve the next PE in the tree | ||
124 | * @pe: current PE | ||
125 | * @root: root PE | ||
126 | * | ||
127 | * The function is used to retrieve the next PE in the | ||
128 | * hierarchy PE tree. | ||
129 | */ | ||
130 | static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, | ||
131 | struct eeh_pe *root) | ||
132 | { | ||
133 | struct list_head *next = pe->child_list.next; | ||
134 | |||
135 | if (next == &pe->child_list) { | ||
136 | while (1) { | ||
137 | if (pe == root) | ||
138 | return NULL; | ||
139 | next = pe->child.next; | ||
140 | if (next != &pe->parent->child_list) | ||
141 | break; | ||
142 | pe = pe->parent; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | return list_entry(next, struct eeh_pe, child); | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * eeh_pe_traverse - Traverse PEs in the specified PHB | ||
151 | * @root: root PE | ||
152 | * @fn: callback | ||
153 | * @flag: extra parameter to callback | ||
154 | * | ||
155 | * The function is used to traverse the specified PE and its | ||
156 | * child PEs. The traversing is to be terminated once the | ||
157 | * callback returns something other than NULL, or no more PEs | ||
158 | * to be traversed. | ||
159 | */ | ||
160 | static void *eeh_pe_traverse(struct eeh_pe *root, | ||
161 | eeh_traverse_func fn, void *flag) | ||
162 | { | ||
163 | struct eeh_pe *pe; | ||
164 | void *ret; | ||
165 | |||
166 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { | ||
167 | ret = fn(pe, flag); | ||
168 | if (ret) return ret; | ||
169 | } | ||
170 | |||
171 | return NULL; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * __eeh_pe_get - Check the PE address | ||
176 | * @data: EEH PE | ||
177 | * @flag: EEH device | ||
178 | * | ||
179 | * For one particular PE, it can be identified by PE address | ||
180 | * or tranditional BDF address. BDF address is composed of | ||
181 | * Bus/Device/Function number. The extra data referred by flag | ||
182 | * indicates which type of address should be used. | ||
183 | */ | ||
184 | static void *__eeh_pe_get(void *data, void *flag) | ||
185 | { | ||
186 | struct eeh_pe *pe = (struct eeh_pe *)data; | ||
187 | struct eeh_dev *edev = (struct eeh_dev *)flag; | ||
188 | |||
189 | /* Unexpected PHB PE */ | ||
190 | if (pe->type == EEH_PE_PHB) | ||
191 | return NULL; | ||
192 | |||
193 | /* We prefer PE address */ | ||
194 | if (edev->pe_config_addr && | ||
195 | (edev->pe_config_addr == pe->addr)) | ||
196 | return pe; | ||
197 | |||
198 | /* Try BDF address */ | ||
199 | if (edev->pe_config_addr && | ||
200 | (edev->config_addr == pe->config_addr)) | ||
201 | return pe; | ||
202 | |||
203 | return NULL; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * eeh_pe_get - Search PE based on the given address | ||
208 | * @edev: EEH device | ||
209 | * | ||
210 | * Search the corresponding PE based on the specified address which | ||
211 | * is included in the eeh device. The function is used to check if | ||
212 | * the associated PE has been created against the PE address. It's | ||
213 | * notable that the PE address has 2 format: traditional PE address | ||
214 | * which is composed of PCI bus/device/function number, or unified | ||
215 | * PE address. | ||
216 | */ | ||
217 | static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev) | ||
218 | { | ||
219 | struct eeh_pe *root = eeh_phb_pe_get(edev->phb); | ||
220 | struct eeh_pe *pe; | ||
221 | |||
222 | eeh_lock(); | ||
223 | pe = eeh_pe_traverse(root, __eeh_pe_get, edev); | ||
224 | eeh_unlock(); | ||
225 | |||
226 | return pe; | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * eeh_pe_get_parent - Retrieve the parent PE | ||
231 | * @edev: EEH device | ||
232 | * | ||
233 | * The whole PEs existing in the system are organized as hierarchy | ||
234 | * tree. The function is used to retrieve the parent PE according | ||
235 | * to the parent EEH device. | ||
236 | */ | ||
237 | static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) | ||
238 | { | ||
239 | struct device_node *dn; | ||
240 | struct eeh_dev *parent; | ||
241 | |||
242 | /* | ||
243 | * It might have the case for the indirect parent | ||
244 | * EEH device already having associated PE, but | ||
245 | * the direct parent EEH device doesn't have yet. | ||
246 | */ | ||
247 | dn = edev->dn->parent; | ||
248 | while (dn) { | ||
249 | /* We're poking out of PCI territory */ | ||
250 | if (!PCI_DN(dn)) return NULL; | ||
251 | |||
252 | parent = of_node_to_eeh_dev(dn); | ||
253 | /* We're poking out of PCI territory */ | ||
254 | if (!parent) return NULL; | ||
255 | |||
256 | if (parent->pe) | ||
257 | return parent->pe; | ||
258 | |||
259 | dn = dn->parent; | ||
260 | } | ||
261 | |||
262 | return NULL; | ||
263 | } | ||