linux-old/lib/rwsem-spinlock.c
<<
>>
Prefs
   1/* rwsem-spinlock.c: R/W semaphores: contention handling functions for generic spinlock
   2 *                                   implementation
   3 *
   4 * Copyright (c) 2001   David Howells (dhowells@redhat.com).
   5 * - Derived partially from idea by Andrea Arcangeli <andrea@suse.de>
   6 * - Derived also from comments by Linus
   7 *
   8 * Trylock by Brian Watson (Brian.J.Watson@compaq.com).
   9 */
  10#include <linux/rwsem.h>
  11#include <linux/sched.h>
  12#include <linux/mm.h>
  13#include <linux/module.h>
  14
  15struct rwsem_waiter {
  16        struct list_head        list;
  17        struct task_struct      *task;
  18        unsigned int            flags;
  19#define RWSEM_WAITING_FOR_READ  0x00000001
  20#define RWSEM_WAITING_FOR_WRITE 0x00000002
  21};
  22
  23#if RWSEM_DEBUG
  24void rwsemtrace(struct rw_semaphore *sem, const char *str)
  25{
  26        if (sem->debug)
  27                printk("[%d] %s({%d,%d})\n",
  28                       current->pid,str,sem->activity,list_empty(&sem->wait_list)?0:1);
  29}
  30#endif
  31
  32/*
  33 * initialise the semaphore
  34 */
  35void fastcall init_rwsem(struct rw_semaphore *sem)
  36{
  37        sem->activity = 0;
  38        spin_lock_init(&sem->wait_lock);
  39        INIT_LIST_HEAD(&sem->wait_list);
  40#if RWSEM_DEBUG
  41        sem->debug = 0;
  42#endif
  43}
  44
  45/*
  46 * handle the lock being released whilst there are processes blocked on it that can now run
  47 * - if we come here, then:
  48 *   - the 'active count' _reached_ zero
  49 *   - the 'waiting count' is non-zero
  50 * - the spinlock must be held by the caller
  51 * - woken process blocks are discarded from the list after having flags zeroised
  52 */
  53static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem)
  54{
  55        struct rwsem_waiter *waiter;
  56        struct task_struct *tsk;
  57        int woken;
  58
  59        rwsemtrace(sem,"Entering __rwsem_do_wake");
  60
  61        waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list);
  62
  63        /* try to grant a single write lock if there's a writer at the front of the queue
  64         * - we leave the 'waiting count' incremented to signify potential contention
  65         */
  66        if (waiter->flags & RWSEM_WAITING_FOR_WRITE) {
  67                sem->activity = -1;
  68                list_del(&waiter->list);
  69                tsk = waiter->task;
  70                mb();
  71                waiter->task = NULL;
  72                wake_up_process(tsk);
  73                free_task_struct(tsk);
  74                goto out;
  75        }
  76
  77        /* grant an infinite number of read locks to the readers at the front of the queue */
  78        woken = 0;
  79        do {
  80                list_del(&waiter->list);
  81                tsk = waiter->task;
  82                mb();
  83                waiter->task = NULL;
  84                wake_up_process(tsk);
  85                free_task_struct(tsk);
  86                woken++;
  87                if (list_empty(&sem->wait_list))
  88                        break;
  89                waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list);
  90        } while (waiter->flags&RWSEM_WAITING_FOR_READ);
  91
  92        sem->activity += woken;
  93
  94 out:
  95        rwsemtrace(sem,"Leaving __rwsem_do_wake");
  96        return sem;
  97}
  98
  99/*
 100 * wake a single writer
 101 */
 102static inline struct rw_semaphore *__rwsem_wake_one_writer(struct rw_semaphore *sem)
 103{
 104        struct rwsem_waiter *waiter;
 105        struct task_struct *tsk;
 106
 107        sem->activity = -1;
 108
 109        waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list);
 110        list_del(&waiter->list);
 111
 112        tsk = waiter->task;
 113        mb();
 114        waiter->task = NULL;
 115        wake_up_process(tsk);
 116        free_task_struct(tsk);
 117        return sem;
 118}
 119
 120/*
 121 * get a read lock on the semaphore
 122 */
 123void fastcall __down_read(struct rw_semaphore *sem)
 124{
 125        struct rwsem_waiter waiter;
 126        struct task_struct *tsk;
 127
 128        rwsemtrace(sem,"Entering __down_read");
 129
 130        spin_lock(&sem->wait_lock);
 131
 132        if (sem->activity>=0 && list_empty(&sem->wait_list)) {
 133                /* granted */
 134                sem->activity++;
 135                spin_unlock(&sem->wait_lock);
 136                goto out;
 137        }
 138
 139        tsk = current;
 140        set_task_state(tsk,TASK_UNINTERRUPTIBLE);
 141
 142        /* set up my own style of waitqueue */
 143        waiter.task = tsk;
 144        waiter.flags = RWSEM_WAITING_FOR_READ;
 145        get_task_struct(tsk);
 146
 147        list_add_tail(&waiter.list,&sem->wait_list);
 148
 149        /* we don't need to touch the semaphore struct anymore */
 150        spin_unlock(&sem->wait_lock);
 151
 152        /* wait to be given the lock */
 153        for (;;) {
 154                if (!waiter.task)
 155                        break;
 156                schedule();
 157                set_task_state(tsk, TASK_UNINTERRUPTIBLE);
 158        }
 159
 160        tsk->state = TASK_RUNNING;
 161
 162 out:
 163        rwsemtrace(sem,"Leaving __down_read");
 164}
 165
 166/*
 167 * trylock for reading -- returns 1 if successful, 0 if contention
 168 */
 169int fastcall __down_read_trylock(struct rw_semaphore *sem)
 170{
 171        int ret = 0;
 172        rwsemtrace(sem,"Entering __down_read_trylock");
 173
 174        spin_lock(&sem->wait_lock);
 175
 176        if (sem->activity>=0 && list_empty(&sem->wait_list)) {
 177                /* granted */
 178                sem->activity++;
 179                ret = 1;
 180        }
 181
 182        spin_unlock(&sem->wait_lock);
 183
 184        rwsemtrace(sem,"Leaving __down_read_trylock");
 185        return ret;
 186}
 187
 188/*
 189 * get a write lock on the semaphore
 190 * - note that we increment the waiting count anyway to indicate an exclusive lock
 191 */
 192void fastcall __down_write(struct rw_semaphore *sem)
 193{
 194        struct rwsem_waiter waiter;
 195        struct task_struct *tsk;
 196
 197        rwsemtrace(sem,"Entering __down_write");
 198
 199        spin_lock(&sem->wait_lock);
 200
 201        if (sem->activity==0 && list_empty(&sem->wait_list)) {
 202                /* granted */
 203                sem->activity = -1;
 204                spin_unlock(&sem->wait_lock);
 205                goto out;
 206        }
 207
 208        tsk = current;
 209        set_task_state(tsk,TASK_UNINTERRUPTIBLE);
 210
 211        /* set up my own style of waitqueue */
 212        waiter.task = tsk;
 213        waiter.flags = RWSEM_WAITING_FOR_WRITE;
 214        get_task_struct(tsk);
 215
 216        list_add_tail(&waiter.list,&sem->wait_list);
 217
 218        /* we don't need to touch the semaphore struct anymore */
 219        spin_unlock(&sem->wait_lock);
 220
 221        /* wait to be given the lock */
 222        for (;;) {
 223                if (!waiter.task)
 224                        break;
 225                schedule();
 226                set_task_state(tsk, TASK_UNINTERRUPTIBLE);
 227        }
 228
 229        tsk->state = TASK_RUNNING;
 230
 231 out:
 232        rwsemtrace(sem,"Leaving __down_write");
 233}
 234
 235/*
 236 * trylock for writing -- returns 1 if successful, 0 if contention
 237 */
 238int fastcall __down_write_trylock(struct rw_semaphore *sem)
 239{
 240        int ret = 0;
 241        rwsemtrace(sem,"Entering __down_write_trylock");
 242
 243        spin_lock(&sem->wait_lock);
 244
 245        if (sem->activity==0 && list_empty(&sem->wait_list)) {
 246                /* granted */
 247                sem->activity = -1;
 248                ret = 1;
 249        }
 250
 251        spin_unlock(&sem->wait_lock);
 252
 253        rwsemtrace(sem,"Leaving __down_write_trylock");
 254        return ret;
 255}
 256
 257/*
 258 * release a read lock on the semaphore
 259 */
 260void fastcall __up_read(struct rw_semaphore *sem)
 261{
 262        rwsemtrace(sem,"Entering __up_read");
 263
 264        spin_lock(&sem->wait_lock);
 265
 266        if (--sem->activity==0 && !list_empty(&sem->wait_list))
 267                sem = __rwsem_wake_one_writer(sem);
 268
 269        spin_unlock(&sem->wait_lock);
 270
 271        rwsemtrace(sem,"Leaving __up_read");
 272}
 273
 274/*
 275 * release a write lock on the semaphore
 276 */
 277void fastcall __up_write(struct rw_semaphore *sem)
 278{
 279        rwsemtrace(sem,"Entering __up_write");
 280
 281        spin_lock(&sem->wait_lock);
 282
 283        sem->activity = 0;
 284        if (!list_empty(&sem->wait_list))
 285                sem = __rwsem_do_wake(sem);
 286
 287        spin_unlock(&sem->wait_lock);
 288
 289        rwsemtrace(sem,"Leaving __up_write");
 290}
 291
 292EXPORT_SYMBOL(init_rwsem);
 293EXPORT_SYMBOL(__down_read);
 294EXPORT_SYMBOL(__down_write);
 295EXPORT_SYMBOL(__up_read);
 296EXPORT_SYMBOL(__up_write);
 297#if RWSEM_DEBUG
 298EXPORT_SYMBOL(rwsemtrace);
 299#endif
 300
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.