linux-old/arch/i386/kernel/semaphore.c
<<
>>
Prefs
   1/*
   2 * i386 semaphore implementation.
   3 *
   4 * (C) Copyright 1999 Linus Torvalds
   5 *
   6 * Portions Copyright 1999 Red Hat, Inc.
   7 *
   8 *      This program is free software; you can redistribute it and/or
   9 *      modify it under the terms of the GNU General Public License
  10 *      as published by the Free Software Foundation; either version
  11 *      2 of the License, or (at your option) any later version.
  12 *
  13 * rw semaphores implemented November 1999 by Benjamin LaHaise <bcrl@redhat.com>
  14 */
  15#include <linux/config.h>
  16#include <linux/sched.h>
  17#include <asm/semaphore.h>
  18
  19/*
  20 * Semaphores are implemented using a two-way counter:
  21 * The "count" variable is decremented for each process
  22 * that tries to acquire the semaphore, while the "sleeping"
  23 * variable is a count of such acquires.
  24 *
  25 * Notably, the inline "up()" and "down()" functions can
  26 * efficiently test if they need to do any extra work (up
  27 * needs to do something only if count was negative before
  28 * the increment operation.
  29 *
  30 * "sleeping" and the contention routine ordering is
  31 * protected by the semaphore spinlock.
  32 *
  33 * Note that these functions are only called when there is
  34 * contention on the lock, and as such all this is the
  35 * "non-critical" part of the whole semaphore business. The
  36 * critical part is the inline stuff in <asm/semaphore.h>
  37 * where we want to avoid any extra jumps and calls.
  38 */
  39
  40/*
  41 * Logic:
  42 *  - only on a boundary condition do we need to care. When we go
  43 *    from a negative count to a non-negative, we wake people up.
  44 *  - when we go from a non-negative count to a negative do we
  45 *    (a) synchronize with the "sleeper" count and (b) make sure
  46 *    that we're on the wakeup list before we synchronize so that
  47 *    we cannot lose wakeup events.
  48 */
  49
  50void __up(struct semaphore *sem)
  51{
  52        wake_up(&sem->wait);
  53}
  54
  55static spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED;
  56
  57void __down(struct semaphore * sem)
  58{
  59        struct task_struct *tsk = current;
  60        DECLARE_WAITQUEUE(wait, tsk);
  61        tsk->state = TASK_UNINTERRUPTIBLE;
  62        add_wait_queue_exclusive(&sem->wait, &wait);
  63
  64        spin_lock_irq(&semaphore_lock);
  65        sem->sleepers++;
  66        for (;;) {
  67                int sleepers = sem->sleepers;
  68
  69                /*
  70                 * Add "everybody else" into it. They aren't
  71                 * playing, because we own the spinlock.
  72                 */
  73                if (!atomic_add_negative(sleepers - 1, &sem->count)) {
  74                        sem->sleepers = 0;
  75                        break;
  76                }
  77                sem->sleepers = 1;      /* us - see -1 above */
  78                spin_unlock_irq(&semaphore_lock);
  79
  80                schedule();
  81                tsk->state = TASK_UNINTERRUPTIBLE;
  82                spin_lock_irq(&semaphore_lock);
  83        }
  84        spin_unlock_irq(&semaphore_lock);
  85        remove_wait_queue(&sem->wait, &wait);
  86        tsk->state = TASK_RUNNING;
  87        wake_up(&sem->wait);
  88}
  89
  90int __down_interruptible(struct semaphore * sem)
  91{
  92        int retval = 0;
  93        struct task_struct *tsk = current;
  94        DECLARE_WAITQUEUE(wait, tsk);
  95        tsk->state = TASK_INTERRUPTIBLE;
  96        add_wait_queue_exclusive(&sem->wait, &wait);
  97
  98        spin_lock_irq(&semaphore_lock);
  99        sem->sleepers ++;
 100        for (;;) {
 101                int sleepers = sem->sleepers;
 102
 103                /*
 104                 * With signals pending, this turns into
 105                 * the trylock failure case - we won't be
 106                 * sleeping, and we* can't get the lock as
 107                 * it has contention. Just correct the count
 108                 * and exit.
 109                 */
 110                if (signal_pending(current)) {
 111                        retval = -EINTR;
 112                        sem->sleepers = 0;
 113                        atomic_add(sleepers, &sem->count);
 114                        break;
 115                }
 116
 117                /*
 118                 * Add "everybody else" into it. They aren't
 119                 * playing, because we own the spinlock. The
 120                 * "-1" is because we're still hoping to get
 121                 * the lock.
 122                 */
 123                if (!atomic_add_negative(sleepers - 1, &sem->count)) {
 124                        sem->sleepers = 0;
 125                        break;
 126                }
 127                sem->sleepers = 1;      /* us - see -1 above */
 128                spin_unlock_irq(&semaphore_lock);
 129
 130                schedule();
 131                tsk->state = TASK_INTERRUPTIBLE;
 132                spin_lock_irq(&semaphore_lock);
 133        }
 134        spin_unlock_irq(&semaphore_lock);
 135        tsk->state = TASK_RUNNING;
 136        remove_wait_queue(&sem->wait, &wait);
 137        wake_up(&sem->wait);
 138        return retval;
 139}
 140
 141/*
 142 * Trylock failed - make sure we correct for
 143 * having decremented the count.
 144 *
 145 * We could have done the trylock with a
 146 * single "cmpxchg" without failure cases,
 147 * but then it wouldn't work on a 386.
 148 */
 149int __down_trylock(struct semaphore * sem)
 150{
 151        int sleepers;
 152        unsigned long flags;
 153
 154        spin_lock_irqsave(&semaphore_lock, flags);
 155        sleepers = sem->sleepers + 1;
 156        sem->sleepers = 0;
 157
 158        /*
 159         * Add "everybody else" and us into it. They aren't
 160         * playing, because we own the spinlock.
 161         */
 162        if (!atomic_add_negative(sleepers, &sem->count))
 163                wake_up(&sem->wait);
 164
 165        spin_unlock_irqrestore(&semaphore_lock, flags);
 166        return 1;
 167}
 168
 169
 170/*
 171 * The semaphore operations have a special calling sequence that
 172 * allow us to do a simpler in-line version of them. These routines
 173 * need to convert that sequence back into the C sequence when
 174 * there is contention on the semaphore.
 175 *
 176 * %ecx contains the semaphore pointer on entry. Save the C-clobbered
 177 * registers (%eax, %edx and %ecx) except %eax when used as a return
 178 * value..
 179 */
 180asm(
 181".text\n"
 182".align 4\n"
 183".globl __down_failed\n"
 184"__down_failed:\n\t"
 185#if defined(CONFIG_FRAME_POINTER)
 186        "pushl %ebp\n\t"
 187        "movl  %esp,%ebp\n\t"
 188#endif
 189        "pushl %eax\n\t"
 190        "pushl %edx\n\t"
 191        "pushl %ecx\n\t"
 192        "call __down\n\t"
 193        "popl %ecx\n\t"
 194        "popl %edx\n\t"
 195        "popl %eax\n\t"
 196#if defined(CONFIG_FRAME_POINTER)
 197        "movl %ebp,%esp\n\t"
 198        "popl %ebp\n\t"
 199#endif
 200        "ret"
 201);
 202
 203asm(
 204".text\n"
 205".align 4\n"
 206".globl __down_failed_interruptible\n"
 207"__down_failed_interruptible:\n\t"
 208#if defined(CONFIG_FRAME_POINTER)
 209        "pushl %ebp\n\t"
 210        "movl  %esp,%ebp\n\t"
 211#endif
 212        "pushl %edx\n\t"
 213        "pushl %ecx\n\t"
 214        "call __down_interruptible\n\t"
 215        "popl %ecx\n\t"
 216        "popl %edx\n\t"
 217#if defined(CONFIG_FRAME_POINTER)
 218        "movl %ebp,%esp\n\t"
 219        "popl %ebp\n\t"
 220#endif
 221        "ret"
 222);
 223
 224asm(
 225".text\n"
 226".align 4\n"
 227".globl __down_failed_trylock\n"
 228"__down_failed_trylock:\n\t"
 229#if defined(CONFIG_FRAME_POINTER)
 230        "pushl %ebp\n\t"
 231        "movl  %esp,%ebp\n\t"
 232#endif
 233        "pushl %edx\n\t"
 234        "pushl %ecx\n\t"
 235        "call __down_trylock\n\t"
 236        "popl %ecx\n\t"
 237        "popl %edx\n\t"
 238#if defined(CONFIG_FRAME_POINTER)
 239        "movl %ebp,%esp\n\t"
 240        "popl %ebp\n\t"
 241#endif
 242        "ret"
 243);
 244
 245asm(
 246".text\n"
 247".align 4\n"
 248".globl __up_wakeup\n"
 249"__up_wakeup:\n\t"
 250        "pushl %eax\n\t"
 251        "pushl %edx\n\t"
 252        "pushl %ecx\n\t"
 253        "call __up\n\t"
 254        "popl %ecx\n\t"
 255        "popl %edx\n\t"
 256        "popl %eax\n\t"
 257        "ret"
 258);
 259
 260/*
 261 * rw spinlock fallbacks
 262 */
 263#if defined(CONFIG_SMP)
 264asm(
 265".text\n"
 266".align 4\n"
 267".globl __write_lock_failed\n"
 268"__write_lock_failed:\n\t"
 269        LOCK "addl      $" RW_LOCK_BIAS_STR ",(%eax)\n"
 270"1:     rep; nop\n\t"
 271        "cmpl   $" RW_LOCK_BIAS_STR ",(%eax)\n\t"
 272        "jne    1b\n\t"
 273        LOCK "subl      $" RW_LOCK_BIAS_STR ",(%eax)\n\t"
 274        "jnz    __write_lock_failed\n\t"
 275        "ret"
 276);
 277
 278asm(
 279".text\n"
 280".align 4\n"
 281".globl __read_lock_failed\n"
 282"__read_lock_failed:\n\t"
 283        LOCK "incl      (%eax)\n"
 284"1:     rep; nop\n\t"
 285        "cmpl   $1,(%eax)\n\t"
 286        "js     1b\n\t"
 287        LOCK "decl      (%eax)\n\t"
 288        "js     __read_lock_failed\n\t"
 289        "ret"
 290);
 291#endif
 292
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.