diff options
Diffstat (limited to 'drivers/misc/sgi-gru/grufault.c')
-rw-r--r-- | drivers/misc/sgi-gru/grufault.c | 89 |
1 files changed, 85 insertions, 4 deletions
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index 7d757e9c42f0..a1b3a1d66af5 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c | |||
@@ -290,6 +290,61 @@ upm: | |||
290 | 290 | ||
291 | 291 | ||
292 | /* | 292 | /* |
293 | * Flush a CBE from cache. The CBE is clean in the cache. Dirty the | ||
294 | * CBE cacheline so that the line will be written back to home agent. | ||
295 | * Otherwise the line may be silently dropped. This has no impact | ||
296 | * except on performance. | ||
297 | */ | ||
298 | static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe) | ||
299 | { | ||
300 | if (unlikely(cbe)) { | ||
301 | cbe->cbrexecstatus = 0; /* make CL dirty */ | ||
302 | gru_flush_cache(cbe); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Preload the TLB with entries that may be required. Currently, preloading | ||
308 | * is implemented only for BCOPY. Preload <tlb_preload_count> pages OR to | ||
309 | * the end of the bcopy tranfer, whichever is smaller. | ||
310 | */ | ||
311 | static void gru_preload_tlb(struct gru_state *gru, | ||
312 | struct gru_thread_state *gts, int atomic, | ||
313 | unsigned long fault_vaddr, int asid, int write, | ||
314 | unsigned char tlb_preload_count, | ||
315 | struct gru_tlb_fault_handle *tfh, | ||
316 | struct gru_control_block_extended *cbe) | ||
317 | { | ||
318 | unsigned long vaddr = 0, gpa; | ||
319 | int ret, pageshift; | ||
320 | |||
321 | if (cbe->opccpy != OP_BCOPY) | ||
322 | return; | ||
323 | |||
324 | if (fault_vaddr == cbe->cbe_baddr0) | ||
325 | vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1; | ||
326 | else if (fault_vaddr == cbe->cbe_baddr1) | ||
327 | vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1; | ||
328 | |||
329 | fault_vaddr &= PAGE_MASK; | ||
330 | vaddr &= PAGE_MASK; | ||
331 | vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE); | ||
332 | |||
333 | while (vaddr > fault_vaddr) { | ||
334 | ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); | ||
335 | if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write, | ||
336 | GRU_PAGESIZE(pageshift))) | ||
337 | return; | ||
338 | gru_dbg(grudev, | ||
339 | "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n", | ||
340 | atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, | ||
341 | vaddr, asid, write, pageshift, gpa); | ||
342 | vaddr -= PAGE_SIZE; | ||
343 | STAT(tlb_preload_page); | ||
344 | } | ||
345 | } | ||
346 | |||
347 | /* | ||
293 | * Drop a TLB entry into the GRU. The fault is described by info in an TFH. | 348 | * Drop a TLB entry into the GRU. The fault is described by info in an TFH. |
294 | * Input: | 349 | * Input: |
295 | * cb Address of user CBR. Null if not running in user context | 350 | * cb Address of user CBR. Null if not running in user context |
@@ -303,6 +358,8 @@ static int gru_try_dropin(struct gru_thread_state *gts, | |||
303 | struct gru_tlb_fault_handle *tfh, | 358 | struct gru_tlb_fault_handle *tfh, |
304 | struct gru_instruction_bits *cbk) | 359 | struct gru_instruction_bits *cbk) |
305 | { | 360 | { |
361 | struct gru_control_block_extended *cbe = NULL; | ||
362 | unsigned char tlb_preload_count = gts->ts_tlb_preload_count; | ||
306 | int pageshift = 0, asid, write, ret, atomic = !cbk, indexway; | 363 | int pageshift = 0, asid, write, ret, atomic = !cbk, indexway; |
307 | unsigned long gpa = 0, vaddr = 0; | 364 | unsigned long gpa = 0, vaddr = 0; |
308 | 365 | ||
@@ -314,6 +371,14 @@ static int gru_try_dropin(struct gru_thread_state *gts, | |||
314 | */ | 371 | */ |
315 | 372 | ||
316 | /* | 373 | /* |
374 | * Prefetch the CBE if doing TLB preloading | ||
375 | */ | ||
376 | if (unlikely(tlb_preload_count)) { | ||
377 | cbe = gru_tfh_to_cbe(tfh); | ||
378 | prefetchw(cbe); | ||
379 | } | ||
380 | |||
381 | /* | ||
317 | * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call. | 382 | * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call. |
318 | * Might be a hardware race OR a stupid user. Ignore FMM because FMM | 383 | * Might be a hardware race OR a stupid user. Ignore FMM because FMM |
319 | * is a transient state. | 384 | * is a transient state. |
@@ -359,6 +424,12 @@ static int gru_try_dropin(struct gru_thread_state *gts, | |||
359 | goto failupm; | 424 | goto failupm; |
360 | } | 425 | } |
361 | } | 426 | } |
427 | |||
428 | if (unlikely(cbe) && pageshift == PAGE_SHIFT) { | ||
429 | gru_preload_tlb(gts->ts_gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe); | ||
430 | gru_flush_cache_cbe(cbe); | ||
431 | } | ||
432 | |||
362 | gru_cb_set_istatus_active(cbk); | 433 | gru_cb_set_istatus_active(cbk); |
363 | tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, | 434 | tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, |
364 | GRU_PAGESIZE(pageshift)); | 435 | GRU_PAGESIZE(pageshift)); |
@@ -378,11 +449,13 @@ failnoasid: | |||
378 | tfh_user_polling_mode(tfh); | 449 | tfh_user_polling_mode(tfh); |
379 | else | 450 | else |
380 | gru_flush_cache(tfh); | 451 | gru_flush_cache(tfh); |
452 | gru_flush_cache_cbe(cbe); | ||
381 | return -EAGAIN; | 453 | return -EAGAIN; |
382 | 454 | ||
383 | failupm: | 455 | failupm: |
384 | /* Atomic failure switch CBR to UPM */ | 456 | /* Atomic failure switch CBR to UPM */ |
385 | tfh_user_polling_mode(tfh); | 457 | tfh_user_polling_mode(tfh); |
458 | gru_flush_cache_cbe(cbe); | ||
386 | STAT(tlb_dropin_fail_upm); | 459 | STAT(tlb_dropin_fail_upm); |
387 | gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); | 460 | gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); |
388 | return 1; | 461 | return 1; |
@@ -390,6 +463,7 @@ failupm: | |||
390 | failfmm: | 463 | failfmm: |
391 | /* FMM state on UPM call */ | 464 | /* FMM state on UPM call */ |
392 | gru_flush_cache(tfh); | 465 | gru_flush_cache(tfh); |
466 | gru_flush_cache_cbe(cbe); | ||
393 | STAT(tlb_dropin_fail_fmm); | 467 | STAT(tlb_dropin_fail_fmm); |
394 | gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state); | 468 | gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state); |
395 | return 0; | 469 | return 0; |
@@ -397,6 +471,7 @@ failfmm: | |||
397 | failnoexception: | 471 | failnoexception: |
398 | /* TFH status did not show exception pending */ | 472 | /* TFH status did not show exception pending */ |
399 | gru_flush_cache(tfh); | 473 | gru_flush_cache(tfh); |
474 | gru_flush_cache_cbe(cbe); | ||
400 | if (cbk) | 475 | if (cbk) |
401 | gru_flush_cache(cbk); | 476 | gru_flush_cache(cbk); |
402 | STAT(tlb_dropin_fail_no_exception); | 477 | STAT(tlb_dropin_fail_no_exception); |
@@ -407,6 +482,7 @@ failnoexception: | |||
407 | failidle: | 482 | failidle: |
408 | /* TFH state was idle - no miss pending */ | 483 | /* TFH state was idle - no miss pending */ |
409 | gru_flush_cache(tfh); | 484 | gru_flush_cache(tfh); |
485 | gru_flush_cache_cbe(cbe); | ||
410 | if (cbk) | 486 | if (cbk) |
411 | gru_flush_cache(cbk); | 487 | gru_flush_cache(cbk); |
412 | STAT(tlb_dropin_fail_idle); | 488 | STAT(tlb_dropin_fail_idle); |
@@ -416,6 +492,7 @@ failidle: | |||
416 | failinval: | 492 | failinval: |
417 | /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */ | 493 | /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */ |
418 | tfh_exception(tfh); | 494 | tfh_exception(tfh); |
495 | gru_flush_cache_cbe(cbe); | ||
419 | STAT(tlb_dropin_fail_invalid); | 496 | STAT(tlb_dropin_fail_invalid); |
420 | gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); | 497 | gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); |
421 | return -EFAULT; | 498 | return -EFAULT; |
@@ -426,6 +503,7 @@ failactive: | |||
426 | tfh_user_polling_mode(tfh); | 503 | tfh_user_polling_mode(tfh); |
427 | else | 504 | else |
428 | gru_flush_cache(tfh); | 505 | gru_flush_cache(tfh); |
506 | gru_flush_cache_cbe(cbe); | ||
429 | STAT(tlb_dropin_fail_range_active); | 507 | STAT(tlb_dropin_fail_range_active); |
430 | gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", | 508 | gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", |
431 | tfh, vaddr); | 509 | tfh, vaddr); |
@@ -627,7 +705,7 @@ int gru_get_exception_detail(unsigned long arg) | |||
627 | excdet.exceptdet1 = cbe->idef3upd; | 705 | excdet.exceptdet1 = cbe->idef3upd; |
628 | excdet.cbrstate = cbe->cbrstate; | 706 | excdet.cbrstate = cbe->cbrstate; |
629 | excdet.cbrexecstatus = cbe->cbrexecstatus; | 707 | excdet.cbrexecstatus = cbe->cbrexecstatus; |
630 | gru_flush_cache(cbe); | 708 | gru_flush_cache_cbe(cbe); |
631 | ret = 0; | 709 | ret = 0; |
632 | } else { | 710 | } else { |
633 | ret = -EAGAIN; | 711 | ret = -EAGAIN; |
@@ -770,9 +848,12 @@ int gru_set_context_option(unsigned long arg) | |||
770 | return -EFAULT; | 848 | return -EFAULT; |
771 | gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1); | 849 | gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1); |
772 | 850 | ||
773 | gts = gru_alloc_locked_gts(req.gseg); | 851 | gts = gru_find_lock_gts(req.gseg); |
774 | if (IS_ERR(gts)) | 852 | if (!gts) { |
775 | return PTR_ERR(gts); | 853 | gts = gru_alloc_locked_gts(req.gseg); |
854 | if (IS_ERR(gts)) | ||
855 | return PTR_ERR(gts); | ||
856 | } | ||
776 | 857 | ||
777 | switch (req.op) { | 858 | switch (req.op) { |
778 | case sco_blade_chiplet: | 859 | case sco_blade_chiplet: |