diff options
| author | Jeff Garzik <jgarzik@pobox.com> | 2005-11-10 04:12:10 -0500 |
|---|---|---|
| committer | Jeff Garzik <jgarzik@pobox.com> | 2005-11-10 04:12:10 -0500 |
| commit | 2f67bdb23d74a6c6fd4f98f64239c5c34d1833cc (patch) | |
| tree | fe533abe3e7c400848647b95e4806f5125c654c3 /arch/s390/mm/fault.c | |
| parent | d40d9d29c020f8466c96f8e3ad4b7c014ff1085d (diff) | |
| parent | 3b44f137b9a846c5452d9e6e1271b79b1dbcc942 (diff) | |
Merge branch 'master'
Diffstat (limited to 'arch/s390/mm/fault.c')
| -rw-r--r-- | arch/s390/mm/fault.c | 115 |
1 files changed, 3 insertions, 112 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 856a971759b1..fb2607c369ed 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
| @@ -160,7 +160,7 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code, | |||
| 160 | * 11 Page translation -> Not present (nullification) | 160 | * 11 Page translation -> Not present (nullification) |
| 161 | * 3b Region third trans. -> Not present (nullification) | 161 | * 3b Region third trans. -> Not present (nullification) |
| 162 | */ | 162 | */ |
| 163 | extern inline void | 163 | static inline void |
| 164 | do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) | 164 | do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) |
| 165 | { | 165 | { |
| 166 | struct task_struct *tsk; | 166 | struct task_struct *tsk; |
| @@ -352,115 +352,6 @@ void do_dat_exception(struct pt_regs *regs, unsigned long error_code) | |||
| 352 | do_exception(regs, error_code & 0xff, 0); | 352 | do_exception(regs, error_code & 0xff, 0); |
| 353 | } | 353 | } |
| 354 | 354 | ||
| 355 | #ifndef CONFIG_ARCH_S390X | ||
| 356 | |||
| 357 | typedef struct _pseudo_wait_t { | ||
| 358 | struct _pseudo_wait_t *next; | ||
| 359 | wait_queue_head_t queue; | ||
| 360 | unsigned long address; | ||
| 361 | int resolved; | ||
| 362 | } pseudo_wait_t; | ||
| 363 | |||
| 364 | static pseudo_wait_t *pseudo_lock_queue = NULL; | ||
| 365 | static spinlock_t pseudo_wait_spinlock; /* spinlock to protect lock queue */ | ||
| 366 | |||
| 367 | /* | ||
| 368 | * This routine handles 'pagex' pseudo page faults. | ||
| 369 | */ | ||
| 370 | asmlinkage void | ||
| 371 | do_pseudo_page_fault(struct pt_regs *regs, unsigned long error_code) | ||
| 372 | { | ||
| 373 | pseudo_wait_t wait_struct; | ||
| 374 | pseudo_wait_t *ptr, *last, *next; | ||
| 375 | unsigned long address; | ||
| 376 | |||
| 377 | /* | ||
| 378 | * get the failing address | ||
| 379 | * more specific the segment and page table portion of | ||
| 380 | * the address | ||
| 381 | */ | ||
| 382 | address = S390_lowcore.trans_exc_code & 0xfffff000; | ||
| 383 | |||
| 384 | if (address & 0x80000000) { | ||
| 385 | /* high bit set -> a page has been swapped in by VM */ | ||
| 386 | address &= 0x7fffffff; | ||
| 387 | spin_lock(&pseudo_wait_spinlock); | ||
| 388 | last = NULL; | ||
| 389 | ptr = pseudo_lock_queue; | ||
| 390 | while (ptr != NULL) { | ||
| 391 | next = ptr->next; | ||
| 392 | if (address == ptr->address) { | ||
| 393 | /* | ||
| 394 | * This is one of the processes waiting | ||
| 395 | * for the page. Unchain from the queue. | ||
| 396 | * There can be more than one process | ||
| 397 | * waiting for the same page. VM presents | ||
| 398 | * an initial and a completion interrupt for | ||
| 399 | * every process that tries to access a | ||
| 400 | * page swapped out by VM. | ||
| 401 | */ | ||
| 402 | if (last == NULL) | ||
| 403 | pseudo_lock_queue = next; | ||
| 404 | else | ||
| 405 | last->next = next; | ||
| 406 | /* now wake up the process */ | ||
| 407 | ptr->resolved = 1; | ||
| 408 | wake_up(&ptr->queue); | ||
| 409 | } else | ||
| 410 | last = ptr; | ||
| 411 | ptr = next; | ||
| 412 | } | ||
| 413 | spin_unlock(&pseudo_wait_spinlock); | ||
| 414 | } else { | ||
| 415 | /* Pseudo page faults in kernel mode is a bad idea */ | ||
| 416 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) { | ||
| 417 | /* | ||
| 418 | * VM presents pseudo page faults if the interrupted | ||
| 419 | * state was not disabled for interrupts. So we can | ||
| 420 | * get pseudo page fault interrupts while running | ||
| 421 | * in kernel mode. We simply access the page here | ||
| 422 | * while we are running disabled. VM will then swap | ||
| 423 | * in the page synchronously. | ||
| 424 | */ | ||
| 425 | if (check_user_space(regs, error_code) == 0) | ||
| 426 | /* dereference a virtual kernel address */ | ||
| 427 | __asm__ __volatile__ ( | ||
| 428 | " ic 0,0(%0)" | ||
| 429 | : : "a" (address) : "0"); | ||
| 430 | else | ||
| 431 | /* dereference a virtual user address */ | ||
| 432 | __asm__ __volatile__ ( | ||
| 433 | " la 2,0(%0)\n" | ||
| 434 | " sacf 512\n" | ||
| 435 | " ic 2,0(2)\n" | ||
| 436 | "0:sacf 0\n" | ||
| 437 | ".section __ex_table,\"a\"\n" | ||
| 438 | " .align 4\n" | ||
| 439 | " .long 0b,0b\n" | ||
| 440 | ".previous" | ||
| 441 | : : "a" (address) : "2" ); | ||
| 442 | |||
| 443 | return; | ||
| 444 | } | ||
| 445 | /* initialize and add element to pseudo_lock_queue */ | ||
| 446 | init_waitqueue_head (&wait_struct.queue); | ||
| 447 | wait_struct.address = address; | ||
| 448 | wait_struct.resolved = 0; | ||
| 449 | spin_lock(&pseudo_wait_spinlock); | ||
| 450 | wait_struct.next = pseudo_lock_queue; | ||
| 451 | pseudo_lock_queue = &wait_struct; | ||
| 452 | spin_unlock(&pseudo_wait_spinlock); | ||
| 453 | /* | ||
| 454 | * The instruction that caused the program check will | ||
| 455 | * be repeated. Don't signal single step via SIGTRAP. | ||
| 456 | */ | ||
| 457 | clear_tsk_thread_flag(current, TIF_SINGLE_STEP); | ||
| 458 | /* go to sleep */ | ||
| 459 | wait_event(wait_struct.queue, wait_struct.resolved); | ||
| 460 | } | ||
| 461 | } | ||
| 462 | #endif /* CONFIG_ARCH_S390X */ | ||
| 463 | |||
| 464 | #ifdef CONFIG_PFAULT | 355 | #ifdef CONFIG_PFAULT |
| 465 | /* | 356 | /* |
| 466 | * 'pfault' pseudo page faults routines. | 357 | * 'pfault' pseudo page faults routines. |
| @@ -508,7 +399,7 @@ int pfault_init(void) | |||
| 508 | " .quad 0b,1b\n" | 399 | " .quad 0b,1b\n" |
| 509 | #endif /* CONFIG_ARCH_S390X */ | 400 | #endif /* CONFIG_ARCH_S390X */ |
| 510 | ".previous" | 401 | ".previous" |
| 511 | : "=d" (rc) : "a" (&refbk) : "cc" ); | 402 | : "=d" (rc) : "a" (&refbk), "m" (refbk) : "cc" ); |
| 512 | __ctl_set_bit(0, 9); | 403 | __ctl_set_bit(0, 9); |
| 513 | return rc; | 404 | return rc; |
| 514 | } | 405 | } |
| @@ -532,7 +423,7 @@ void pfault_fini(void) | |||
| 532 | " .quad 0b,0b\n" | 423 | " .quad 0b,0b\n" |
| 533 | #endif /* CONFIG_ARCH_S390X */ | 424 | #endif /* CONFIG_ARCH_S390X */ |
| 534 | ".previous" | 425 | ".previous" |
| 535 | : : "a" (&refbk) : "cc" ); | 426 | : : "a" (&refbk), "m" (refbk) : "cc" ); |
| 536 | } | 427 | } |
| 537 | 428 | ||
| 538 | asmlinkage void | 429 | asmlinkage void |
