diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c new file mode 100644 index 000000000000..80306bc8c799 --- /dev/null +++ b/arch/s390/mm/fault.c | |||
@@ -0,0 +1,586 @@ | |||
1 | /* | ||
2 | * arch/s390/mm/fault.c | ||
3 | * | ||
4 | * S390 version | ||
5 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
6 | * Author(s): Hartmut Penner (hp@de.ibm.com) | ||
7 | * Ulrich Weigand (uweigand@de.ibm.com) | ||
8 | * | ||
9 | * Derived from "arch/i386/mm/fault.c" | ||
10 | * Copyright (C) 1995 Linus Torvalds | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/signal.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/ptrace.h> | ||
21 | #include <linux/mman.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/smp.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/console.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/hardirq.h> | ||
29 | |||
30 | #include <asm/system.h> | ||
31 | #include <asm/uaccess.h> | ||
32 | #include <asm/pgtable.h> | ||
33 | |||
34 | #ifndef CONFIG_ARCH_S390X | ||
35 | #define __FAIL_ADDR_MASK 0x7ffff000 | ||
36 | #define __FIXUP_MASK 0x7fffffff | ||
37 | #define __SUBCODE_MASK 0x0200 | ||
38 | #define __PF_RES_FIELD 0ULL | ||
39 | #else /* CONFIG_ARCH_S390X */ | ||
40 | #define __FAIL_ADDR_MASK -4096L | ||
41 | #define __FIXUP_MASK ~0L | ||
42 | #define __SUBCODE_MASK 0x0600 | ||
43 | #define __PF_RES_FIELD 0x8000000000000000ULL | ||
44 | #endif /* CONFIG_ARCH_S390X */ | ||
45 | |||
46 | #ifdef CONFIG_SYSCTL | ||
47 | extern int sysctl_userprocess_debug; | ||
48 | #endif | ||
49 | |||
50 | extern void die(const char *,struct pt_regs *,long); | ||
51 | |||
52 | extern spinlock_t timerlist_lock; | ||
53 | |||
54 | /* | ||
55 | * Unlock any spinlocks which will prevent us from getting the | ||
56 | * message out (timerlist_lock is acquired through the | ||
57 | * console unblank code) | ||
58 | */ | ||
59 | void bust_spinlocks(int yes) | ||
60 | { | ||
61 | if (yes) { | ||
62 | oops_in_progress = 1; | ||
63 | } else { | ||
64 | int loglevel_save = console_loglevel; | ||
65 | console_unblank(); | ||
66 | oops_in_progress = 0; | ||
67 | /* | ||
68 | * OK, the message is on the console. Now we call printk() | ||
69 | * without oops_in_progress set so that printk will give klogd | ||
70 | * a poke. Hold onto your hats... | ||
71 | */ | ||
72 | console_loglevel = 15; | ||
73 | printk(" "); | ||
74 | console_loglevel = loglevel_save; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Check which address space is addressed by the access | ||
80 | * register in S390_lowcore.exc_access_id. | ||
81 | * Returns 1 for user space and 0 for kernel space. | ||
82 | */ | ||
83 | static int __check_access_register(struct pt_regs *regs, int error_code) | ||
84 | { | ||
85 | int areg = S390_lowcore.exc_access_id; | ||
86 | |||
87 | if (areg == 0) | ||
88 | /* Access via access register 0 -> kernel address */ | ||
89 | return 0; | ||
90 | save_access_regs(current->thread.acrs); | ||
91 | if (regs && areg < NUM_ACRS && current->thread.acrs[areg] <= 1) | ||
92 | /* | ||
93 | * access register contains 0 -> kernel address, | ||
94 | * access register contains 1 -> user space address | ||
95 | */ | ||
96 | return current->thread.acrs[areg]; | ||
97 | |||
98 | /* Something unhealthy was done with the access registers... */ | ||
99 | die("page fault via unknown access register", regs, error_code); | ||
100 | do_exit(SIGKILL); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * Check which address space the address belongs to. | ||
106 | * Returns 1 for user space and 0 for kernel space. | ||
107 | */ | ||
108 | static inline int check_user_space(struct pt_regs *regs, int error_code) | ||
109 | { | ||
110 | /* | ||
111 | * The lowest two bits of S390_lowcore.trans_exc_code indicate | ||
112 | * which paging table was used: | ||
113 | * 0: Primary Segment Table Descriptor | ||
114 | * 1: STD determined via access register | ||
115 | * 2: Secondary Segment Table Descriptor | ||
116 | * 3: Home Segment Table Descriptor | ||
117 | */ | ||
118 | int descriptor = S390_lowcore.trans_exc_code & 3; | ||
119 | if (unlikely(descriptor == 1)) | ||
120 | return __check_access_register(regs, error_code); | ||
121 | if (descriptor == 2) | ||
122 | return current->thread.mm_segment.ar4; | ||
123 | return descriptor != 0; | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Send SIGSEGV to task. This is an external routine | ||
128 | * to keep the stack usage of do_page_fault small. | ||
129 | */ | ||
130 | static void do_sigsegv(struct pt_regs *regs, unsigned long error_code, | ||
131 | int si_code, unsigned long address) | ||
132 | { | ||
133 | struct siginfo si; | ||
134 | |||
135 | #if defined(CONFIG_SYSCTL) || defined(CONFIG_PROCESS_DEBUG) | ||
136 | #if defined(CONFIG_SYSCTL) | ||
137 | if (sysctl_userprocess_debug) | ||
138 | #endif | ||
139 | { | ||
140 | printk("User process fault: interruption code 0x%lX\n", | ||
141 | error_code); | ||
142 | printk("failing address: %lX\n", address); | ||
143 | show_regs(regs); | ||
144 | } | ||
145 | #endif | ||
146 | si.si_signo = SIGSEGV; | ||
147 | si.si_code = si_code; | ||
148 | si.si_addr = (void *) address; | ||
149 | force_sig_info(SIGSEGV, &si, current); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * This routine handles page faults. It determines the address, | ||
154 | * and the problem, and then passes it off to one of the appropriate | ||
155 | * routines. | ||
156 | * | ||
157 | * error_code: | ||
158 | * 04 Protection -> Write-Protection (suprression) | ||
159 | * 10 Segment translation -> Not present (nullification) | ||
160 | * 11 Page translation -> Not present (nullification) | ||
161 | * 3b Region third trans. -> Not present (nullification) | ||
162 | */ | ||
163 | extern inline void | ||
164 | do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) | ||
165 | { | ||
166 | struct task_struct *tsk; | ||
167 | struct mm_struct *mm; | ||
168 | struct vm_area_struct * vma; | ||
169 | unsigned long address; | ||
170 | int user_address; | ||
171 | const struct exception_table_entry *fixup; | ||
172 | int si_code = SEGV_MAPERR; | ||
173 | |||
174 | tsk = current; | ||
175 | mm = tsk->mm; | ||
176 | |||
177 | /* | ||
178 | * Check for low-address protection. This needs to be treated | ||
179 | * as a special case because the translation exception code | ||
180 | * field is not guaranteed to contain valid data in this case. | ||
181 | */ | ||
182 | if (is_protection && !(S390_lowcore.trans_exc_code & 4)) { | ||
183 | |||
184 | /* Low-address protection hit in kernel mode means | ||
185 | NULL pointer write access in kernel mode. */ | ||
186 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) { | ||
187 | address = 0; | ||
188 | user_address = 0; | ||
189 | goto no_context; | ||
190 | } | ||
191 | |||
192 | /* Low-address protection hit in user mode 'cannot happen'. */ | ||
193 | die ("Low-address protection", regs, error_code); | ||
194 | do_exit(SIGKILL); | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * get the failing address | ||
199 | * more specific the segment and page table portion of | ||
200 | * the address | ||
201 | */ | ||
202 | address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK; | ||
203 | user_address = check_user_space(regs, error_code); | ||
204 | |||
205 | /* | ||
206 | * Verify that the fault happened in user space, that | ||
207 | * we are not in an interrupt and that there is a | ||
208 | * user context. | ||
209 | */ | ||
210 | if (user_address == 0 || in_interrupt() || !mm) | ||
211 | goto no_context; | ||
212 | |||
213 | /* | ||
214 | * When we get here, the fault happened in the current | ||
215 | * task's user address space, so we can switch on the | ||
216 | * interrupts again and then search the VMAs | ||
217 | */ | ||
218 | local_irq_enable(); | ||
219 | |||
220 | down_read(&mm->mmap_sem); | ||
221 | |||
222 | vma = find_vma(mm, address); | ||
223 | if (!vma) | ||
224 | goto bad_area; | ||
225 | if (vma->vm_start <= address) | ||
226 | goto good_area; | ||
227 | if (!(vma->vm_flags & VM_GROWSDOWN)) | ||
228 | goto bad_area; | ||
229 | if (expand_stack(vma, address)) | ||
230 | goto bad_area; | ||
231 | /* | ||
232 | * Ok, we have a good vm_area for this memory access, so | ||
233 | * we can handle it.. | ||
234 | */ | ||
235 | good_area: | ||
236 | si_code = SEGV_ACCERR; | ||
237 | if (!is_protection) { | ||
238 | /* page not present, check vm flags */ | ||
239 | if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) | ||
240 | goto bad_area; | ||
241 | } else { | ||
242 | if (!(vma->vm_flags & VM_WRITE)) | ||
243 | goto bad_area; | ||
244 | } | ||
245 | |||
246 | survive: | ||
247 | /* | ||
248 | * If for any reason at all we couldn't handle the fault, | ||
249 | * make sure we exit gracefully rather than endlessly redo | ||
250 | * the fault. | ||
251 | */ | ||
252 | switch (handle_mm_fault(mm, vma, address, is_protection)) { | ||
253 | case VM_FAULT_MINOR: | ||
254 | tsk->min_flt++; | ||
255 | break; | ||
256 | case VM_FAULT_MAJOR: | ||
257 | tsk->maj_flt++; | ||
258 | break; | ||
259 | case VM_FAULT_SIGBUS: | ||
260 | goto do_sigbus; | ||
261 | case VM_FAULT_OOM: | ||
262 | goto out_of_memory; | ||
263 | default: | ||
264 | BUG(); | ||
265 | } | ||
266 | |||
267 | up_read(&mm->mmap_sem); | ||
268 | /* | ||
269 | * The instruction that caused the program check will | ||
270 | * be repeated. Don't signal single step via SIGTRAP. | ||
271 | */ | ||
272 | clear_tsk_thread_flag(current, TIF_SINGLE_STEP); | ||
273 | return; | ||
274 | |||
275 | /* | ||
276 | * Something tried to access memory that isn't in our memory map.. | ||
277 | * Fix it, but check if it's kernel or user first.. | ||
278 | */ | ||
279 | bad_area: | ||
280 | up_read(&mm->mmap_sem); | ||
281 | |||
282 | /* User mode accesses just cause a SIGSEGV */ | ||
283 | if (regs->psw.mask & PSW_MASK_PSTATE) { | ||
284 | tsk->thread.prot_addr = address; | ||
285 | tsk->thread.trap_no = error_code; | ||
286 | do_sigsegv(regs, error_code, si_code, address); | ||
287 | return; | ||
288 | } | ||
289 | |||
290 | no_context: | ||
291 | /* Are we prepared to handle this kernel fault? */ | ||
292 | fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK); | ||
293 | if (fixup) { | ||
294 | regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE; | ||
295 | return; | ||
296 | } | ||
297 | |||
298 | /* | ||
299 | * Oops. The kernel tried to access some bad page. We'll have to | ||
300 | * terminate things with extreme prejudice. | ||
301 | */ | ||
302 | if (user_address == 0) | ||
303 | printk(KERN_ALERT "Unable to handle kernel pointer dereference" | ||
304 | " at virtual kernel address %p\n", (void *)address); | ||
305 | else | ||
306 | printk(KERN_ALERT "Unable to handle kernel paging request" | ||
307 | " at virtual user address %p\n", (void *)address); | ||
308 | |||
309 | die("Oops", regs, error_code); | ||
310 | do_exit(SIGKILL); | ||
311 | |||
312 | |||
313 | /* | ||
314 | * We ran out of memory, or some other thing happened to us that made | ||
315 | * us unable to handle the page fault gracefully. | ||
316 | */ | ||
317 | out_of_memory: | ||
318 | up_read(&mm->mmap_sem); | ||
319 | if (tsk->pid == 1) { | ||
320 | yield(); | ||
321 | goto survive; | ||
322 | } | ||
323 | printk("VM: killing process %s\n", tsk->comm); | ||
324 | if (regs->psw.mask & PSW_MASK_PSTATE) | ||
325 | do_exit(SIGKILL); | ||
326 | goto no_context; | ||
327 | |||
328 | do_sigbus: | ||
329 | up_read(&mm->mmap_sem); | ||
330 | |||
331 | /* | ||
332 | * Send a sigbus, regardless of whether we were in kernel | ||
333 | * or user mode. | ||
334 | */ | ||
335 | tsk->thread.prot_addr = address; | ||
336 | tsk->thread.trap_no = error_code; | ||
337 | force_sig(SIGBUS, tsk); | ||
338 | |||
339 | /* Kernel mode? Handle exceptions or die */ | ||
340 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) | ||
341 | goto no_context; | ||
342 | } | ||
343 | |||
344 | void do_protection_exception(struct pt_regs *regs, unsigned long error_code) | ||
345 | { | ||
346 | regs->psw.addr -= (error_code >> 16); | ||
347 | do_exception(regs, 4, 1); | ||
348 | } | ||
349 | |||
350 | void do_dat_exception(struct pt_regs *regs, unsigned long error_code) | ||
351 | { | ||
352 | do_exception(regs, error_code & 0xff, 0); | ||
353 | } | ||
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 | ||
465 | /* | ||
466 | * 'pfault' pseudo page faults routines. | ||
467 | */ | ||
468 | static int pfault_disable = 0; | ||
469 | |||
470 | static int __init nopfault(char *str) | ||
471 | { | ||
472 | pfault_disable = 1; | ||
473 | return 1; | ||
474 | } | ||
475 | |||
476 | __setup("nopfault", nopfault); | ||
477 | |||
478 | typedef struct { | ||
479 | __u16 refdiagc; | ||
480 | __u16 reffcode; | ||
481 | __u16 refdwlen; | ||
482 | __u16 refversn; | ||
483 | __u64 refgaddr; | ||
484 | __u64 refselmk; | ||
485 | __u64 refcmpmk; | ||
486 | __u64 reserved; | ||
487 | } __attribute__ ((packed)) pfault_refbk_t; | ||
488 | |||
489 | int pfault_init(void) | ||
490 | { | ||
491 | pfault_refbk_t refbk = | ||
492 | { 0x258, 0, 5, 2, __LC_CURRENT, 1ULL << 48, 1ULL << 48, | ||
493 | __PF_RES_FIELD }; | ||
494 | int rc; | ||
495 | |||
496 | if (pfault_disable) | ||
497 | return -1; | ||
498 | __asm__ __volatile__( | ||
499 | " diag %1,%0,0x258\n" | ||
500 | "0: j 2f\n" | ||
501 | "1: la %0,8\n" | ||
502 | "2:\n" | ||
503 | ".section __ex_table,\"a\"\n" | ||
504 | " .align 4\n" | ||
505 | #ifndef CONFIG_ARCH_S390X | ||
506 | " .long 0b,1b\n" | ||
507 | #else /* CONFIG_ARCH_S390X */ | ||
508 | " .quad 0b,1b\n" | ||
509 | #endif /* CONFIG_ARCH_S390X */ | ||
510 | ".previous" | ||
511 | : "=d" (rc) : "a" (&refbk) : "cc" ); | ||
512 | __ctl_set_bit(0, 9); | ||
513 | return rc; | ||
514 | } | ||
515 | |||
516 | void pfault_fini(void) | ||
517 | { | ||
518 | pfault_refbk_t refbk = | ||
519 | { 0x258, 1, 5, 2, 0ULL, 0ULL, 0ULL, 0ULL }; | ||
520 | |||
521 | if (pfault_disable) | ||
522 | return; | ||
523 | __ctl_clear_bit(0,9); | ||
524 | __asm__ __volatile__( | ||
525 | " diag %0,0,0x258\n" | ||
526 | "0:\n" | ||
527 | ".section __ex_table,\"a\"\n" | ||
528 | " .align 4\n" | ||
529 | #ifndef CONFIG_ARCH_S390X | ||
530 | " .long 0b,0b\n" | ||
531 | #else /* CONFIG_ARCH_S390X */ | ||
532 | " .quad 0b,0b\n" | ||
533 | #endif /* CONFIG_ARCH_S390X */ | ||
534 | ".previous" | ||
535 | : : "a" (&refbk) : "cc" ); | ||
536 | } | ||
537 | |||
538 | asmlinkage void | ||
539 | pfault_interrupt(struct pt_regs *regs, __u16 error_code) | ||
540 | { | ||
541 | struct task_struct *tsk; | ||
542 | __u16 subcode; | ||
543 | |||
544 | /* | ||
545 | * Get the external interruption subcode & pfault | ||
546 | * initial/completion signal bit. VM stores this | ||
547 | * in the 'cpu address' field associated with the | ||
548 | * external interrupt. | ||
549 | */ | ||
550 | subcode = S390_lowcore.cpu_addr; | ||
551 | if ((subcode & 0xff00) != __SUBCODE_MASK) | ||
552 | return; | ||
553 | |||
554 | /* | ||
555 | * Get the token (= address of the task structure of the affected task). | ||
556 | */ | ||
557 | tsk = *(struct task_struct **) __LC_PFAULT_INTPARM; | ||
558 | |||
559 | if (subcode & 0x0080) { | ||
560 | /* signal bit is set -> a page has been swapped in by VM */ | ||
561 | if (xchg(&tsk->thread.pfault_wait, -1) != 0) { | ||
562 | /* Initial interrupt was faster than the completion | ||
563 | * interrupt. pfault_wait is valid. Set pfault_wait | ||
564 | * back to zero and wake up the process. This can | ||
565 | * safely be done because the task is still sleeping | ||
566 | * and can't procude new pfaults. */ | ||
567 | tsk->thread.pfault_wait = 0; | ||
568 | wake_up_process(tsk); | ||
569 | } | ||
570 | } else { | ||
571 | /* signal bit not set -> a real page is missing. */ | ||
572 | set_task_state(tsk, TASK_UNINTERRUPTIBLE); | ||
573 | if (xchg(&tsk->thread.pfault_wait, 1) != 0) { | ||
574 | /* Completion interrupt was faster than the initial | ||
575 | * interrupt (swapped in a -1 for pfault_wait). Set | ||
576 | * pfault_wait back to zero and exit. This can be | ||
577 | * done safely because tsk is running in kernel | ||
578 | * mode and can't produce new pfaults. */ | ||
579 | tsk->thread.pfault_wait = 0; | ||
580 | set_task_state(tsk, TASK_RUNNING); | ||
581 | } else | ||
582 | set_tsk_need_resched(tsk); | ||
583 | } | ||
584 | } | ||
585 | #endif | ||
586 | |||