diff options
| author | Jack Steiner <steiner@sgi.com> | 2009-12-15 19:48:11 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-16 10:20:15 -0500 |
| commit | 67bf04a5c2574e9495f660f418f6df776821d578 (patch) | |
| tree | ff28ab4983b007136da88786c8966ea1598841a0 /drivers/misc | |
| parent | e006043a4d2da52bba9fd9cb7e5a22e2951ff69b (diff) | |
gru: fix prefetch and speculation bugs
Fix several bugs related to prefetch, ordering & speculation:
- GRU cch_allocate() instruction causes cacheable memory
to be created. Add a barriers to prevent speculation
from prefetching data before it exists.
- Add memory barriers before cache-flush instructions to ensure
that previously stored data is included in the line flushed to memory.
Signed-off-by: Jack Steiner <steiner@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/misc')
| -rw-r--r-- | drivers/misc/sgi-gru/gru_instructions.h | 1 | ||||
| -rw-r--r-- | drivers/misc/sgi-gru/grufault.c | 2 | ||||
| -rw-r--r-- | drivers/misc/sgi-gru/gruhandles.c | 22 | ||||
| -rw-r--r-- | drivers/misc/sgi-gru/grukservices.c | 10 | ||||
| -rw-r--r-- | drivers/misc/sgi-gru/grumain.c | 9 |
5 files changed, 38 insertions, 6 deletions
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h index e033b6ce4a3f..32f358d9f429 100644 --- a/drivers/misc/sgi-gru/gru_instructions.h +++ b/drivers/misc/sgi-gru/gru_instructions.h | |||
| @@ -325,6 +325,7 @@ static inline void gru_flush_cache(void *p) | |||
| 325 | static inline void gru_start_instruction(struct gru_instruction *ins, int op32) | 325 | static inline void gru_start_instruction(struct gru_instruction *ins, int op32) |
| 326 | { | 326 | { |
| 327 | gru_ordered_store_int(ins, op32); | 327 | gru_ordered_store_int(ins, op32); |
| 328 | mb(); | ||
| 328 | gru_flush_cache(ins); | 329 | gru_flush_cache(ins); |
| 329 | } | 330 | } |
| 330 | 331 | ||
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index 7466234450f3..d3cacd696b38 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c | |||
| @@ -333,6 +333,7 @@ static int gru_try_dropin(struct gru_thread_state *gts, | |||
| 333 | */ | 333 | */ |
| 334 | if (tfh->status != TFHSTATUS_EXCEPTION) { | 334 | if (tfh->status != TFHSTATUS_EXCEPTION) { |
| 335 | gru_flush_cache(tfh); | 335 | gru_flush_cache(tfh); |
| 336 | sync_core(); | ||
| 336 | if (tfh->status != TFHSTATUS_EXCEPTION) | 337 | if (tfh->status != TFHSTATUS_EXCEPTION) |
| 337 | goto failnoexception; | 338 | goto failnoexception; |
| 338 | STAT(tfh_stale_on_fault); | 339 | STAT(tfh_stale_on_fault); |
| @@ -599,6 +600,7 @@ int gru_get_exception_detail(unsigned long arg) | |||
| 599 | cbrnum = thread_cbr_number(gts, ucbnum); | 600 | cbrnum = thread_cbr_number(gts, ucbnum); |
| 600 | cbe = get_cbe_by_index(gts->ts_gru, cbrnum); | 601 | cbe = get_cbe_by_index(gts->ts_gru, cbrnum); |
| 601 | gru_flush_cache(cbe); /* CBE not coherent */ | 602 | gru_flush_cache(cbe); /* CBE not coherent */ |
| 603 | sync_core(); /* make sure we are have current data */ | ||
| 602 | excdet.opc = cbe->opccpy; | 604 | excdet.opc = cbe->opccpy; |
| 603 | excdet.exopc = cbe->exopccpy; | 605 | excdet.exopc = cbe->exopccpy; |
| 604 | excdet.ecause = cbe->ecause; | 606 | excdet.ecause = cbe->ecause; |
diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c index 806419a6b44c..f1117a7637a2 100644 --- a/drivers/misc/sgi-gru/gruhandles.c +++ b/drivers/misc/sgi-gru/gruhandles.c | |||
| @@ -91,9 +91,18 @@ static int wait_instruction_complete(void *h, enum mcs_op opc) | |||
| 91 | 91 | ||
| 92 | int cch_allocate(struct gru_context_configuration_handle *cch) | 92 | int cch_allocate(struct gru_context_configuration_handle *cch) |
| 93 | { | 93 | { |
| 94 | int ret; | ||
| 95 | |||
| 94 | cch->opc = CCHOP_ALLOCATE; | 96 | cch->opc = CCHOP_ALLOCATE; |
| 95 | start_instruction(cch); | 97 | start_instruction(cch); |
| 96 | return wait_instruction_complete(cch, cchop_allocate); | 98 | ret = wait_instruction_complete(cch, cchop_allocate); |
| 99 | |||
| 100 | /* | ||
| 101 | * Stop speculation into the GSEG being mapped by the previous ALLOCATE. | ||
| 102 | * The GSEG memory does not exist until the ALLOCATE completes. | ||
| 103 | */ | ||
| 104 | sync_core(); | ||
| 105 | return ret; | ||
| 97 | } | 106 | } |
| 98 | 107 | ||
| 99 | int cch_start(struct gru_context_configuration_handle *cch) | 108 | int cch_start(struct gru_context_configuration_handle *cch) |
| @@ -112,9 +121,18 @@ int cch_interrupt(struct gru_context_configuration_handle *cch) | |||
| 112 | 121 | ||
| 113 | int cch_deallocate(struct gru_context_configuration_handle *cch) | 122 | int cch_deallocate(struct gru_context_configuration_handle *cch) |
| 114 | { | 123 | { |
| 124 | int ret; | ||
| 125 | |||
| 115 | cch->opc = CCHOP_DEALLOCATE; | 126 | cch->opc = CCHOP_DEALLOCATE; |
| 116 | start_instruction(cch); | 127 | start_instruction(cch); |
| 117 | return wait_instruction_complete(cch, cchop_deallocate); | 128 | ret = wait_instruction_complete(cch, cchop_deallocate); |
| 129 | |||
| 130 | /* | ||
| 131 | * Stop speculation into the GSEG being unmapped by the previous | ||
| 132 | * DEALLOCATE. | ||
| 133 | */ | ||
| 134 | sync_core(); | ||
| 135 | return ret; | ||
| 118 | } | 136 | } |
| 119 | 137 | ||
| 120 | int cch_interrupt_sync(struct gru_context_configuration_handle | 138 | int cch_interrupt_sync(struct gru_context_configuration_handle |
diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index 24ec109e61cc..8c81aca0463a 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c | |||
| @@ -395,6 +395,7 @@ int gru_get_cb_exception_detail(void *cb, | |||
| 395 | cbrnum = thread_cbr_number(bs->bs_kgts, get_cb_number(cb)); | 395 | cbrnum = thread_cbr_number(bs->bs_kgts, get_cb_number(cb)); |
| 396 | cbe = get_cbe(GRUBASE(cb), cbrnum); | 396 | cbe = get_cbe(GRUBASE(cb), cbrnum); |
| 397 | gru_flush_cache(cbe); /* CBE not coherent */ | 397 | gru_flush_cache(cbe); /* CBE not coherent */ |
| 398 | sync_core(); | ||
| 398 | excdet->opc = cbe->opccpy; | 399 | excdet->opc = cbe->opccpy; |
| 399 | excdet->exopc = cbe->exopccpy; | 400 | excdet->exopc = cbe->exopccpy; |
| 400 | excdet->ecause = cbe->ecause; | 401 | excdet->ecause = cbe->ecause; |
| @@ -461,9 +462,10 @@ int gru_check_status_proc(void *cb) | |||
| 461 | int ret; | 462 | int ret; |
| 462 | 463 | ||
| 463 | ret = gen->istatus; | 464 | ret = gen->istatus; |
| 464 | if (ret != CBS_EXCEPTION) | 465 | if (ret == CBS_EXCEPTION) |
| 465 | return ret; | 466 | ret = gru_retry_exception(cb); |
| 466 | return gru_retry_exception(cb); | 467 | rmb(); |
| 468 | return ret; | ||
| 467 | 469 | ||
| 468 | } | 470 | } |
| 469 | 471 | ||
| @@ -475,7 +477,7 @@ int gru_wait_proc(void *cb) | |||
| 475 | ret = gru_wait_idle_or_exception(gen); | 477 | ret = gru_wait_idle_or_exception(gen); |
| 476 | if (ret == CBS_EXCEPTION) | 478 | if (ret == CBS_EXCEPTION) |
| 477 | ret = gru_retry_exception(cb); | 479 | ret = gru_retry_exception(cb); |
| 478 | 480 | rmb(); | |
| 479 | return ret; | 481 | return ret; |
| 480 | } | 482 | } |
| 481 | 483 | ||
diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c index 944028871884..a383271d3912 100644 --- a/drivers/misc/sgi-gru/grumain.c +++ b/drivers/misc/sgi-gru/grumain.c | |||
| @@ -499,6 +499,9 @@ static void gru_load_context_data(void *save, void *grubase, int ctxnum, | |||
| 499 | memset(cbe + i * GRU_HANDLE_STRIDE, 0, | 499 | memset(cbe + i * GRU_HANDLE_STRIDE, 0, |
| 500 | GRU_CACHE_LINE_BYTES); | 500 | GRU_CACHE_LINE_BYTES); |
| 501 | } | 501 | } |
| 502 | /* Flush CBE to hide race in context restart */ | ||
| 503 | mb(); | ||
| 504 | gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); | ||
| 502 | cb += GRU_HANDLE_STRIDE; | 505 | cb += GRU_HANDLE_STRIDE; |
| 503 | } | 506 | } |
| 504 | 507 | ||
| @@ -519,6 +522,12 @@ static void gru_unload_context_data(void *save, void *grubase, int ctxnum, | |||
| 519 | cb = gseg + GRU_CB_BASE; | 522 | cb = gseg + GRU_CB_BASE; |
| 520 | cbe = grubase + GRU_CBE_BASE; | 523 | cbe = grubase + GRU_CBE_BASE; |
| 521 | length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; | 524 | length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; |
| 525 | |||
| 526 | /* CBEs may not be coherent. Flush them from cache */ | ||
| 527 | for_each_cbr_in_allocation_map(i, &cbrmap, scr) | ||
| 528 | gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); | ||
| 529 | mb(); /* Let the CL flush complete */ | ||
| 530 | |||
| 522 | gru_prefetch_context(gseg, cb, cbe, cbrmap, length); | 531 | gru_prefetch_context(gseg, cb, cbe, cbrmap, length); |
| 523 | 532 | ||
| 524 | for_each_cbr_in_allocation_map(i, &cbrmap, scr) { | 533 | for_each_cbr_in_allocation_map(i, &cbrmap, scr) { |
