| /* |
| * i386 semaphore implementation. |
| * |
| * (C) Copyright 1999 Linus Torvalds |
| * |
| * Portions Copyright 1999 Red Hat, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version |
| * 2 of the License, or (at your option) any later version. |
| * |
| * rw semaphores implemented November 1999 by Benjamin LaHaise <bcrl@kvack.org> |
| */ |
| #include <linux/config.h> |
| #include <asm/semaphore.h> |
| |
| /* |
| * The semaphore operations have a special calling sequence that |
| * allow us to do a simpler in-line version of them. These routines |
| * need to convert that sequence back into the C sequence when |
| * there is contention on the semaphore. |
| * |
| * %eax contains the semaphore pointer on entry. Save the C-clobbered |
| * registers (%eax, %edx and %ecx) except %eax whish is either a return |
| * value or just clobbered.. |
| */ |
| asm( |
| ".section .sched.text\n" |
| ".align 4\n" |
| ".globl __down_failed\n" |
| "__down_failed:\n\t" |
| #if defined(CONFIG_FRAME_POINTER) |
| "pushl %ebp\n\t" |
| "movl %esp,%ebp\n\t" |
| #endif |
| "pushl %edx\n\t" |
| "pushl %ecx\n\t" |
| "call __down\n\t" |
| "popl %ecx\n\t" |
| "popl %edx\n\t" |
| #if defined(CONFIG_FRAME_POINTER) |
| "movl %ebp,%esp\n\t" |
| "popl %ebp\n\t" |
| #endif |
| "ret" |
| ); |
| |
| asm( |
| ".section .sched.text\n" |
| ".align 4\n" |
| ".globl __down_failed_interruptible\n" |
| "__down_failed_interruptible:\n\t" |
| #if defined(CONFIG_FRAME_POINTER) |
| "pushl %ebp\n\t" |
| "movl %esp,%ebp\n\t" |
| #endif |
| "pushl %edx\n\t" |
| "pushl %ecx\n\t" |
| "call __down_interruptible\n\t" |
| "popl %ecx\n\t" |
| "popl %edx\n\t" |
| #if defined(CONFIG_FRAME_POINTER) |
| "movl %ebp,%esp\n\t" |
| "popl %ebp\n\t" |
| #endif |
| "ret" |
| ); |
| |
| asm( |
| ".section .sched.text\n" |
| ".align 4\n" |
| ".globl __down_failed_trylock\n" |
| "__down_failed_trylock:\n\t" |
| #if defined(CONFIG_FRAME_POINTER) |
| "pushl %ebp\n\t" |
| "movl %esp,%ebp\n\t" |
| #endif |
| "pushl %edx\n\t" |
| "pushl %ecx\n\t" |
| "call __down_trylock\n\t" |
| "popl %ecx\n\t" |
| "popl %edx\n\t" |
| #if defined(CONFIG_FRAME_POINTER) |
| "movl %ebp,%esp\n\t" |
| "popl %ebp\n\t" |
| #endif |
| "ret" |
| ); |
| |
| asm( |
| ".section .sched.text\n" |
| ".align 4\n" |
| ".globl __up_wakeup\n" |
| "__up_wakeup:\n\t" |
| "pushl %edx\n\t" |
| "pushl %ecx\n\t" |
| "call __up\n\t" |
| "popl %ecx\n\t" |
| "popl %edx\n\t" |
| "ret" |
| ); |
| |
| /* |
| * rw spinlock fallbacks |
| */ |
| #if defined(CONFIG_SMP) |
| asm( |
| ".section .sched.text\n" |
| ".align 4\n" |
| ".globl __write_lock_failed\n" |
| "__write_lock_failed:\n\t" |
| LOCK_PREFIX "addl $" RW_LOCK_BIAS_STR ",(%eax)\n" |
| "1: rep; nop\n\t" |
| "cmpl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" |
| "jne 1b\n\t" |
| LOCK_PREFIX "subl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" |
| "jnz __write_lock_failed\n\t" |
| "ret" |
| ); |
| |
| asm( |
| ".section .sched.text\n" |
| ".align 4\n" |
| ".globl __read_lock_failed\n" |
| "__read_lock_failed:\n\t" |
| LOCK_PREFIX "incl (%eax)\n" |
| "1: rep; nop\n\t" |
| "cmpl $1,(%eax)\n\t" |
| "js 1b\n\t" |
| LOCK_PREFIX "decl (%eax)\n\t" |
| "js __read_lock_failed\n\t" |
| "ret" |
| ); |
| #endif |