diff options
author | Gavin Shan <shangw@linux.vnet.ibm.com> | 2012-09-11 15:16:18 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-09-18 01:32:33 -0400 |
commit | ea81245cf4dab5ef5428102a4a00281ed6e890a6 (patch) | |
tree | eb75479f80d1f24014f8b6f624e35f9e89d59067 /arch/powerpc/platforms | |
parent | 20ee6a970858a0f5711ea32cb7f855a81704cb53 (diff) |
powerpc/eeh: Global mutex to protect PE tree
We have missed lots of situations where the PE hierarchy tree need
protection through the EEH global mutex. The patch fixes that for
those public APIs implemented in eeh_pe.c. The only exception is
eeh_pe_restore_bars() because it calls eeh_pe_dev_traverse(), which
has been protected by the mutex.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_pe.c | 39 |
1 files changed, 32 insertions, 7 deletions
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c index 30b8d96ed54d..9d35543736ed 100644 --- a/arch/powerpc/platforms/pseries/eeh_pe.c +++ b/arch/powerpc/platforms/pseries/eeh_pe.c | |||
@@ -99,8 +99,6 @@ static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb) | |||
99 | { | 99 | { |
100 | struct eeh_pe *pe; | 100 | struct eeh_pe *pe; |
101 | 101 | ||
102 | eeh_lock(); | ||
103 | |||
104 | list_for_each_entry(pe, &eeh_phb_pe, child) { | 102 | list_for_each_entry(pe, &eeh_phb_pe, child) { |
105 | /* | 103 | /* |
106 | * Actually, we needn't check the type since | 104 | * Actually, we needn't check the type since |
@@ -114,8 +112,6 @@ static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb) | |||
114 | } | 112 | } |
115 | } | 113 | } |
116 | 114 | ||
117 | eeh_unlock(); | ||
118 | |||
119 | return NULL; | 115 | return NULL; |
120 | } | 116 | } |
121 | 117 | ||
@@ -192,14 +188,21 @@ void *eeh_pe_dev_traverse(struct eeh_pe *root, | |||
192 | return NULL; | 188 | return NULL; |
193 | } | 189 | } |
194 | 190 | ||
191 | eeh_lock(); | ||
192 | |||
195 | /* Traverse root PE */ | 193 | /* Traverse root PE */ |
196 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { | 194 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { |
197 | eeh_pe_for_each_dev(pe, edev) { | 195 | eeh_pe_for_each_dev(pe, edev) { |
198 | ret = fn(edev, flag); | 196 | ret = fn(edev, flag); |
199 | if (ret) return ret; | 197 | if (ret) { |
198 | eeh_unlock(); | ||
199 | return ret; | ||
200 | } | ||
200 | } | 201 | } |
201 | } | 202 | } |
202 | 203 | ||
204 | eeh_unlock(); | ||
205 | |||
203 | return NULL; | 206 | return NULL; |
204 | } | 207 | } |
205 | 208 | ||
@@ -251,9 +254,7 @@ static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev) | |||
251 | struct eeh_pe *root = eeh_phb_pe_get(edev->phb); | 254 | struct eeh_pe *root = eeh_phb_pe_get(edev->phb); |
252 | struct eeh_pe *pe; | 255 | struct eeh_pe *pe; |
253 | 256 | ||
254 | eeh_lock(); | ||
255 | pe = eeh_pe_traverse(root, __eeh_pe_get, edev); | 257 | pe = eeh_pe_traverse(root, __eeh_pe_get, edev); |
256 | eeh_unlock(); | ||
257 | 258 | ||
258 | return pe; | 259 | return pe; |
259 | } | 260 | } |
@@ -307,6 +308,8 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
307 | { | 308 | { |
308 | struct eeh_pe *pe, *parent; | 309 | struct eeh_pe *pe, *parent; |
309 | 310 | ||
311 | eeh_lock(); | ||
312 | |||
310 | /* | 313 | /* |
311 | * Search the PE has been existing or not according | 314 | * Search the PE has been existing or not according |
312 | * to the PE address. If that has been existing, the | 315 | * to the PE address. If that has been existing, the |
@@ -316,6 +319,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
316 | pe = eeh_pe_get(edev); | 319 | pe = eeh_pe_get(edev); |
317 | if (pe && !(pe->type & EEH_PE_INVALID)) { | 320 | if (pe && !(pe->type & EEH_PE_INVALID)) { |
318 | if (!edev->pe_config_addr) { | 321 | if (!edev->pe_config_addr) { |
322 | eeh_unlock(); | ||
319 | pr_err("%s: PE with addr 0x%x already exists\n", | 323 | pr_err("%s: PE with addr 0x%x already exists\n", |
320 | __func__, edev->config_addr); | 324 | __func__, edev->config_addr); |
321 | return -EEXIST; | 325 | return -EEXIST; |
@@ -327,6 +331,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
327 | 331 | ||
328 | /* Put the edev to PE */ | 332 | /* Put the edev to PE */ |
329 | list_add_tail(&edev->list, &pe->edevs); | 333 | list_add_tail(&edev->list, &pe->edevs); |
334 | eeh_unlock(); | ||
330 | pr_debug("EEH: Add %s to Bus PE#%x\n", | 335 | pr_debug("EEH: Add %s to Bus PE#%x\n", |
331 | edev->dn->full_name, pe->addr); | 336 | edev->dn->full_name, pe->addr); |
332 | 337 | ||
@@ -345,6 +350,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
345 | parent->type &= ~EEH_PE_INVALID; | 350 | parent->type &= ~EEH_PE_INVALID; |
346 | parent = parent->parent; | 351 | parent = parent->parent; |
347 | } | 352 | } |
353 | eeh_unlock(); | ||
348 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", | 354 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", |
349 | edev->dn->full_name, pe->addr, pe->parent->addr); | 355 | edev->dn->full_name, pe->addr, pe->parent->addr); |
350 | 356 | ||
@@ -354,6 +360,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
354 | /* Create a new EEH PE */ | 360 | /* Create a new EEH PE */ |
355 | pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE); | 361 | pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE); |
356 | if (!pe) { | 362 | if (!pe) { |
363 | eeh_unlock(); | ||
357 | pr_err("%s: out of memory!\n", __func__); | 364 | pr_err("%s: out of memory!\n", __func__); |
358 | return -ENOMEM; | 365 | return -ENOMEM; |
359 | } | 366 | } |
@@ -370,6 +377,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
370 | if (!parent) { | 377 | if (!parent) { |
371 | parent = eeh_phb_pe_get(edev->phb); | 378 | parent = eeh_phb_pe_get(edev->phb); |
372 | if (!parent) { | 379 | if (!parent) { |
380 | eeh_unlock(); | ||
373 | pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", | 381 | pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", |
374 | __func__, edev->phb->global_number); | 382 | __func__, edev->phb->global_number); |
375 | edev->pe = NULL; | 383 | edev->pe = NULL; |
@@ -386,6 +394,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
386 | list_add_tail(&pe->child, &parent->child_list); | 394 | list_add_tail(&pe->child, &parent->child_list); |
387 | list_add_tail(&edev->list, &pe->edevs); | 395 | list_add_tail(&edev->list, &pe->edevs); |
388 | edev->pe = pe; | 396 | edev->pe = pe; |
397 | eeh_unlock(); | ||
389 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", | 398 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", |
390 | edev->dn->full_name, pe->addr, pe->parent->addr); | 399 | edev->dn->full_name, pe->addr, pe->parent->addr); |
391 | 400 | ||
@@ -413,6 +422,8 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) | |||
413 | return -EEXIST; | 422 | return -EEXIST; |
414 | } | 423 | } |
415 | 424 | ||
425 | eeh_lock(); | ||
426 | |||
416 | /* Remove the EEH device */ | 427 | /* Remove the EEH device */ |
417 | pe = edev->pe; | 428 | pe = edev->pe; |
418 | edev->pe = NULL; | 429 | edev->pe = NULL; |
@@ -457,6 +468,8 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) | |||
457 | pe = parent; | 468 | pe = parent; |
458 | } | 469 | } |
459 | 470 | ||
471 | eeh_unlock(); | ||
472 | |||
460 | return 0; | 473 | return 0; |
461 | } | 474 | } |
462 | 475 | ||
@@ -502,7 +515,9 @@ static void *__eeh_pe_state_mark(void *data, void *flag) | |||
502 | */ | 515 | */ |
503 | void eeh_pe_state_mark(struct eeh_pe *pe, int state) | 516 | void eeh_pe_state_mark(struct eeh_pe *pe, int state) |
504 | { | 517 | { |
518 | eeh_lock(); | ||
505 | eeh_pe_traverse(pe, __eeh_pe_state_mark, &state); | 519 | eeh_pe_traverse(pe, __eeh_pe_state_mark, &state); |
520 | eeh_unlock(); | ||
506 | } | 521 | } |
507 | 522 | ||
508 | /** | 523 | /** |
@@ -536,7 +551,9 @@ static void *__eeh_pe_state_clear(void *data, void *flag) | |||
536 | */ | 551 | */ |
537 | void eeh_pe_state_clear(struct eeh_pe *pe, int state) | 552 | void eeh_pe_state_clear(struct eeh_pe *pe, int state) |
538 | { | 553 | { |
554 | eeh_lock(); | ||
539 | eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); | 555 | eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); |
556 | eeh_unlock(); | ||
540 | } | 557 | } |
541 | 558 | ||
542 | /** | 559 | /** |
@@ -598,6 +615,10 @@ static void *eeh_restore_one_device_bars(void *data, void *flag) | |||
598 | */ | 615 | */ |
599 | void eeh_pe_restore_bars(struct eeh_pe *pe) | 616 | void eeh_pe_restore_bars(struct eeh_pe *pe) |
600 | { | 617 | { |
618 | /* | ||
619 | * We needn't take the EEH lock since eeh_pe_dev_traverse() | ||
620 | * will take that. | ||
621 | */ | ||
601 | eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL); | 622 | eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL); |
602 | } | 623 | } |
603 | 624 | ||
@@ -617,6 +638,8 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe) | |||
617 | struct eeh_dev *edev; | 638 | struct eeh_dev *edev; |
618 | struct pci_dev *pdev; | 639 | struct pci_dev *pdev; |
619 | 640 | ||
641 | eeh_lock(); | ||
642 | |||
620 | if (pe->type & EEH_PE_PHB) { | 643 | if (pe->type & EEH_PE_PHB) { |
621 | bus = pe->phb->bus; | 644 | bus = pe->phb->bus; |
622 | } else if (pe->type & EEH_PE_BUS) { | 645 | } else if (pe->type & EEH_PE_BUS) { |
@@ -626,5 +649,7 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe) | |||
626 | bus = pdev->bus; | 649 | bus = pdev->bus; |
627 | } | 650 | } |
628 | 651 | ||
652 | eeh_unlock(); | ||
653 | |||
629 | return bus; | 654 | return bus; |
630 | } | 655 | } |