aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/sgi-gru
diff options
context:
space:
mode:
authorJack Steiner <steiner@sgi.com>2009-12-15 19:48:03 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-16 10:20:14 -0500
commitb61fc69bb61838e99b368d208e6e2ee363eba2a1 (patch)
treee499fe1d706cab16561551b3ff49f9f5450659b1 /drivers/misc/sgi-gru
parent091f1a10614db89a108cda980425799730d73d8a (diff)
gru: fix istatus race in GRU tlb dropin
TLB dropins require updates to the CBR instruction istatus field. This is needed to resolve race conditions in the chip. The code currently uses the user address of the CBR. This works but opens up additional endcases related to stealing of contexts and accessing the CBR from tasks that do not have access to the user address space. (Some of this non-user task access is debug code that is not currently being pushed to the community). User CBRs are also directly accessible using the kernel mapping of the CBR. Change the TLB dropin code to use the the kernel mapping of the CBR. 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/sgi-gru')
-rw-r--r--drivers/misc/sgi-gru/grufault.c49
1 files changed, 20 insertions, 29 deletions
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
index 2605edfbaebe..a9f0cf483005 100644
--- a/drivers/misc/sgi-gru/grufault.c
+++ b/drivers/misc/sgi-gru/grufault.c
@@ -122,22 +122,11 @@ static void gru_unlock_gts(struct gru_thread_state *gts)
122 * is necessary to prevent the user from seeing a stale cb.istatus that will 122 * is necessary to prevent the user from seeing a stale cb.istatus that will
123 * change as soon as the TFH restart is complete. Races may cause an 123 * change as soon as the TFH restart is complete. Races may cause an
124 * occasional failure to clear the cb.istatus, but that is ok. 124 * occasional failure to clear the cb.istatus, but that is ok.
125 *
126 * If the cb address is not valid (should not happen, but...), nothing
127 * bad will happen.. The get_user()/put_user() will fail but there
128 * are no bad side-effects.
129 */ 125 */
130static void gru_cb_set_istatus_active(unsigned long __user *cb) 126static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk)
131{ 127{
132 union { 128 if (cbk) {
133 struct gru_instruction_bits bits; 129 cbk->istatus = CBS_ACTIVE;
134 unsigned long dw;
135 } u;
136
137 if (cb) {
138 get_user(u.dw, cb);
139 u.bits.istatus = CBS_ACTIVE;
140 put_user(u.dw, cb);
141 } 130 }
142} 131}
143 132
@@ -322,9 +311,9 @@ upm:
322 */ 311 */
323static int gru_try_dropin(struct gru_thread_state *gts, 312static int gru_try_dropin(struct gru_thread_state *gts,
324 struct gru_tlb_fault_handle *tfh, 313 struct gru_tlb_fault_handle *tfh,
325 unsigned long __user *cb) 314 struct gru_instruction_bits *cbk)
326{ 315{
327 int pageshift = 0, asid, write, ret, atomic = !cb; 316 int pageshift = 0, asid, write, ret, atomic = !cbk;
328 unsigned long gpa = 0, vaddr = 0; 317 unsigned long gpa = 0, vaddr = 0;
329 318
330 /* 319 /*
@@ -347,7 +336,7 @@ static int gru_try_dropin(struct gru_thread_state *gts,
347 } 336 }
348 if (tfh->state == TFHSTATE_IDLE) 337 if (tfh->state == TFHSTATE_IDLE)
349 goto failidle; 338 goto failidle;
350 if (tfh->state == TFHSTATE_MISS_FMM && cb) 339 if (tfh->state == TFHSTATE_MISS_FMM && cbk)
351 goto failfmm; 340 goto failfmm;
352 341
353 write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0; 342 write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0;
@@ -378,7 +367,7 @@ static int gru_try_dropin(struct gru_thread_state *gts,
378 goto failupm; 367 goto failupm;
379 } 368 }
380 } 369 }
381 gru_cb_set_istatus_active(cb); 370 gru_cb_set_istatus_active(cbk);
382 tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, 371 tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write,
383 GRU_PAGESIZE(pageshift)); 372 GRU_PAGESIZE(pageshift));
384 STAT(tlb_dropin); 373 STAT(tlb_dropin);
@@ -392,7 +381,7 @@ failnoasid:
392 /* No asid (delayed unload). */ 381 /* No asid (delayed unload). */
393 STAT(tlb_dropin_fail_no_asid); 382 STAT(tlb_dropin_fail_no_asid);
394 gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); 383 gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
395 if (!cb) 384 if (!cbk)
396 tfh_user_polling_mode(tfh); 385 tfh_user_polling_mode(tfh);
397 else 386 else
398 gru_flush_cache(tfh); 387 gru_flush_cache(tfh);
@@ -415,17 +404,18 @@ failfmm:
415failnoexception: 404failnoexception:
416 /* TFH status did not show exception pending */ 405 /* TFH status did not show exception pending */
417 gru_flush_cache(tfh); 406 gru_flush_cache(tfh);
418 if (cb) 407 if (cbk)
419 gru_flush_cache(cb); 408 gru_flush_cache(cbk);
420 STAT(tlb_dropin_fail_no_exception); 409 STAT(tlb_dropin_fail_no_exception);
421 gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", tfh, tfh->status, tfh->state); 410 gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n",
411 tfh, tfh->status, tfh->state);
422 return 0; 412 return 0;
423 413
424failidle: 414failidle:
425 /* TFH state was idle - no miss pending */ 415 /* TFH state was idle - no miss pending */
426 gru_flush_cache(tfh); 416 gru_flush_cache(tfh);
427 if (cb) 417 if (cbk)
428 gru_flush_cache(cb); 418 gru_flush_cache(cbk);
429 STAT(tlb_dropin_fail_idle); 419 STAT(tlb_dropin_fail_idle);
430 gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state); 420 gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state);
431 return 0; 421 return 0;
@@ -439,7 +429,7 @@ failinval:
439 429
440failactive: 430failactive:
441 /* Range invalidate active. Switch to UPM iff atomic */ 431 /* Range invalidate active. Switch to UPM iff atomic */
442 if (!cb) 432 if (!cbk)
443 tfh_user_polling_mode(tfh); 433 tfh_user_polling_mode(tfh);
444 else 434 else
445 gru_flush_cache(tfh); 435 gru_flush_cache(tfh);
@@ -512,7 +502,7 @@ irqreturn_t gru_intr(int irq, void *dev_id)
512 502
513static int gru_user_dropin(struct gru_thread_state *gts, 503static int gru_user_dropin(struct gru_thread_state *gts,
514 struct gru_tlb_fault_handle *tfh, 504 struct gru_tlb_fault_handle *tfh,
515 unsigned long __user *cb) 505 void *cb)
516{ 506{
517 struct gru_mm_struct *gms = gts->ts_gms; 507 struct gru_mm_struct *gms = gts->ts_gms;
518 int ret; 508 int ret;
@@ -538,7 +528,7 @@ int gru_handle_user_call_os(unsigned long cb)
538{ 528{
539 struct gru_tlb_fault_handle *tfh; 529 struct gru_tlb_fault_handle *tfh;
540 struct gru_thread_state *gts; 530 struct gru_thread_state *gts;
541 unsigned long __user *cbp; 531 void *cbk;
542 int ucbnum, cbrnum, ret = -EINVAL; 532 int ucbnum, cbrnum, ret = -EINVAL;
543 533
544 STAT(call_os); 534 STAT(call_os);
@@ -548,7 +538,6 @@ int gru_handle_user_call_os(unsigned long cb)
548 ucbnum = get_cb_number((void *)cb); 538 ucbnum = get_cb_number((void *)cb);
549 if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB) 539 if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB)
550 return -EINVAL; 540 return -EINVAL;
551 cbp = (unsigned long *)cb;
552 541
553 gts = gru_find_lock_gts(cb); 542 gts = gru_find_lock_gts(cb);
554 if (!gts) 543 if (!gts)
@@ -583,7 +572,9 @@ int gru_handle_user_call_os(unsigned long cb)
583 gru_unload_context(gts, 1); 572 gru_unload_context(gts, 1);
584 } else if (gts->ts_gru) { 573 } else if (gts->ts_gru) {
585 tfh = get_tfh_by_index(gts->ts_gru, cbrnum); 574 tfh = get_tfh_by_index(gts->ts_gru, cbrnum);
586 ret = gru_user_dropin(gts, tfh, cbp); 575 cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr,
576 gts->ts_ctxnum, ucbnum);
577 ret = gru_user_dropin(gts, tfh, cbk);
587 } 578 }
588exit: 579exit:
589 gru_unlock_gts(gts); 580 gru_unlock_gts(gts);