fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / include / asm-parisc / spinlock.h
index a114d46..f3d2090 100644 (file)
 #define __ASM_SPINLOCK_H
 
 #include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/spinlock_types.h>
 
-/* Note that PA-RISC has to use `1' to mean unlocked and `0' to mean locked
- * since it only has load-and-zero.
- */
-
-#undef SPIN_LOCK_UNLOCKED
-#define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 }
-
-#define spin_lock_init(x)      do { (x)->lock = 1; } while(0)
-
-#define spin_is_locked(x) ((x)->lock == 0)
+static inline int __raw_spin_is_locked(raw_spinlock_t *x)
+{
+       volatile unsigned int *a = __ldcw_align(x);
+       return *a == 0;
+}
 
-#define spin_unlock_wait(x)    do { barrier(); } while(((volatile spinlock_t *)(x))->lock == 0)
+#define __raw_spin_lock(lock) __raw_spin_lock_flags(lock, 0)
+#define __raw_spin_unlock_wait(x) \
+               do { cpu_relax(); } while (__raw_spin_is_locked(x))
 
-#if 1
-#define _raw_spin_lock(x) do { \
-       while (__ldcw (&(x)->lock) == 0) \
-               while (((x)->lock) == 0) ; } while (0)
+static inline void __raw_spin_lock_flags(raw_spinlock_t *x,
+                                        unsigned long flags)
+{
+       volatile unsigned int *a;
+
+       mb();
+       a = __ldcw_align(x);
+       while (__ldcw(a) == 0)
+               while (*a == 0)
+                       if (flags & PSW_SM_I) {
+                               local_irq_enable();
+                               cpu_relax();
+                               local_irq_disable();
+                       } else
+                               cpu_relax();
+       mb();
+}
 
-#else
-#define _raw_spin_lock(x) \
-       do { while(__ldcw(&(x)->lock) == 0); } while(0)
-#endif
-       
-#define _raw_spin_unlock(x) \
-       do { (x)->lock = 1; } while(0)
+static inline void __raw_spin_unlock(raw_spinlock_t *x)
+{
+       volatile unsigned int *a;
+       mb();
+       a = __ldcw_align(x);
+       *a = 1;
+       mb();
+}
 
-#define _raw_spin_trylock(x) (__ldcw(&(x)->lock) != 0)
+static inline int __raw_spin_trylock(raw_spinlock_t *x)
+{
+       volatile unsigned int *a;
+       int ret;
 
+       mb();
+       a = __ldcw_align(x);
+        ret = __ldcw(a) != 0;
+       mb();
 
+       return ret;
+}
 
 /*
- * Read-write spinlocks, allowing multiple readers
- * but only one writer.
+ * Read-write spinlocks, allowing multiple readers but only one writer.
+ * Linux rwlocks are unfair to writers; they can be starved for an indefinite
+ * time by readers.  With care, they can also be taken in interrupt context.
+ *
+ * In the PA-RISC implementation, we have a spinlock and a counter.
+ * Readers use the lock to serialise their access to the counter (which
+ * records how many readers currently hold the lock).
+ * Writers hold the spinlock, preventing any readers or other writers from
+ * grabbing the rwlock.
  */
-typedef struct {
-       spinlock_t lock;
-       volatile int counter;
-} rwlock_t;
 
-#define RW_LOCK_UNLOCKED (rwlock_t) { {1}, 0 }
-
-#define rwlock_init(lp)        do { *(lp) = RW_LOCK_UNLOCKED; } while (0)
-
-#define rwlock_is_locked(lp) ((lp)->counter != 0)
-
-/* read_lock, read_unlock are pretty straightforward.  Of course it somehow
- * sucks we end up saving/restoring flags twice for read_lock_irqsave aso. */
-
-static  __inline__ void _raw_read_lock(rwlock_t *rw)
+/* Note that we have to ensure interrupts are disabled in case we're
+ * interrupted by some other code that wants to grab the same read lock */
+static  __inline__ void __raw_read_lock(raw_rwlock_t *rw)
 {
        unsigned long flags;
        local_irq_save(flags);
-       _raw_spin_lock(&rw->lock); 
-
+       __raw_spin_lock_flags(&rw->lock, flags);
        rw->counter++;
-
-       _raw_spin_unlock(&rw->lock);
+       __raw_spin_unlock(&rw->lock);
        local_irq_restore(flags);
 }
 
-static  __inline__ void _raw_read_unlock(rwlock_t *rw)
+/* Note that we have to ensure interrupts are disabled in case we're
+ * interrupted by some other code that wants to grab the same read lock */
+static  __inline__ void __raw_read_unlock(raw_rwlock_t *rw)
 {
        unsigned long flags;
        local_irq_save(flags);
-       _raw_spin_lock(&rw->lock); 
-
+       __raw_spin_lock_flags(&rw->lock, flags);
        rw->counter--;
-
-       _raw_spin_unlock(&rw->lock);
+       __raw_spin_unlock(&rw->lock);
        local_irq_restore(flags);
 }
 
-/* write_lock is less trivial.  We optimistically grab the lock and check
- * if we surprised any readers.  If so we release the lock and wait till
- * they're all gone before trying again
- *
- * Also note that we don't use the _irqsave / _irqrestore suffixes here.
- * If we're called with interrupts enabled and we've got readers (or other
- * writers) in interrupt handlers someone fucked up and we'd dead-lock
- * sooner or later anyway.   prumpf */
+/* Note that we have to ensure interrupts are disabled in case we're
+ * interrupted by some other code that wants to grab the same read lock */
+static __inline__ int __raw_read_trylock(raw_rwlock_t *rw)
+{
+       unsigned long flags;
+ retry:
+       local_irq_save(flags);
+       if (__raw_spin_trylock(&rw->lock)) {
+               rw->counter++;
+               __raw_spin_unlock(&rw->lock);
+               local_irq_restore(flags);
+               return 1;
+       }
+
+       local_irq_restore(flags);
+       /* If write-locked, we fail to acquire the lock */
+       if (rw->counter < 0)
+               return 0;
+
+       /* Wait until we have a realistic chance at the lock */
+       while (__raw_spin_is_locked(&rw->lock) && rw->counter >= 0)
+               cpu_relax();
+
+       goto retry;
+}
 
-static  __inline__ void _raw_write_lock(rwlock_t *rw)
+/* Note that we have to ensure interrupts are disabled in case we're
+ * interrupted by some other code that wants to read_trylock() this lock */
+static __inline__ void __raw_write_lock(raw_rwlock_t *rw)
 {
+       unsigned long flags;
 retry:
-       _raw_spin_lock(&rw->lock);
+       local_irq_save(flags);
+       __raw_spin_lock_flags(&rw->lock, flags);
 
-       if(rw->counter != 0) {
-               /* this basically never happens */
-               _raw_spin_unlock(&rw->lock);
+       if (rw->counter != 0) {
+               __raw_spin_unlock(&rw->lock);
+               local_irq_restore(flags);
 
-               while(rw->counter != 0);
+               while (rw->counter != 0)
+                       cpu_relax();
 
                goto retry;
        }
 
-       /* got it.  now leave without unlocking */
-       rw->counter = -1; /* remember we are locked */
+       rw->counter = -1; /* mark as write-locked */
+       mb();
+       local_irq_restore(flags);
 }
 
-/* write_unlock is absolutely trivial - we don't have to wait for anything */
-
-static  __inline__ void _raw_write_unlock(rwlock_t *rw)
+static __inline__ void __raw_write_unlock(raw_rwlock_t *rw)
 {
        rw->counter = 0;
-       _raw_spin_unlock(&rw->lock);
+       __raw_spin_unlock(&rw->lock);
+}
+
+/* Note that we have to ensure interrupts are disabled in case we're
+ * interrupted by some other code that wants to read_trylock() this lock */
+static __inline__ int __raw_write_trylock(raw_rwlock_t *rw)
+{
+       unsigned long flags;
+       int result = 0;
+
+       local_irq_save(flags);
+       if (__raw_spin_trylock(&rw->lock)) {
+               if (rw->counter == 0) {
+                       rw->counter = -1;
+                       result = 1;
+               } else {
+                       /* Read-locked.  Oh well. */
+                       __raw_spin_unlock(&rw->lock);
+               }
+       }
+       local_irq_restore(flags);
+
+       return result;
 }
 
-static __inline__ int is_read_locked(rwlock_t *rw)
+/*
+ * read_can_lock - would read_trylock() succeed?
+ * @lock: the rwlock in question.
+ */
+static __inline__ int __raw_read_can_lock(raw_rwlock_t *rw)
 {
-       return rw->counter > 0;
+       return rw->counter >= 0;
 }
 
-static __inline__ int is_write_locked(rwlock_t *rw)
+/*
+ * write_can_lock - would write_trylock() succeed?
+ * @lock: the rwlock in question.
+ */
+static __inline__ int __raw_write_can_lock(raw_rwlock_t *rw)
 {
-       return rw->counter < 0;
+       return !rw->counter;
 }
 
+#define _raw_spin_relax(lock)  cpu_relax()
+#define _raw_read_relax(lock)  cpu_relax()
+#define _raw_write_relax(lock) cpu_relax()
+
 #endif /* __ASM_SPINLOCK_H */