These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / kernel / locking / qrwlock.c
index f956ede..fec0823 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Queue read/write lock
+ * Queued read/write locks
  *
  * 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
 #include <linux/hardirq.h>
 #include <asm/qrwlock.h>
 
+/*
+ * This internal data structure is used for optimizing access to some of
+ * the subfields within the atomic_t cnts.
+ */
+struct __qrwlock {
+       union {
+               atomic_t cnts;
+               struct {
+#ifdef __LITTLE_ENDIAN
+                       u8 wmode;       /* Writer mode   */
+                       u8 rcnts[3];    /* Reader counts */
+#else
+                       u8 rcnts[3];    /* Reader counts */
+                       u8 wmode;       /* Writer mode   */
+#endif
+               };
+       };
+       arch_spinlock_t lock;
+};
+
 /**
  * rspin_until_writer_unlock - inc reader count & spin until writer is gone
  * @lock  : Pointer to queue rwlock structure
@@ -35,27 +55,29 @@ rspin_until_writer_unlock(struct qrwlock *lock, u32 cnts)
 {
        while ((cnts & _QW_WMASK) == _QW_LOCKED) {
                cpu_relax_lowlatency();
-               cnts = smp_load_acquire((u32 *)&lock->cnts);
+               cnts = atomic_read_acquire(&lock->cnts);
        }
 }
 
 /**
- * queue_read_lock_slowpath - acquire read lock of a queue rwlock
+ * queued_read_lock_slowpath - acquire read lock of a queue rwlock
  * @lock: Pointer to queue rwlock structure
+ * @cnts: Current qrwlock lock value
  */
-void queue_read_lock_slowpath(struct qrwlock *lock)
+void queued_read_lock_slowpath(struct qrwlock *lock, u32 cnts)
 {
-       u32 cnts;
-
        /*
         * Readers come here when they cannot get the lock without waiting
         */
        if (unlikely(in_interrupt())) {
                /*
-                * Readers in interrupt context will spin until the lock is
-                * available without waiting in the queue.
+                * Readers in interrupt context will get the lock immediately
+                * if the writer is just waiting (not holding the lock yet).
+                * The rspin_until_writer_unlock() function returns immediately
+                * in this case. Otherwise, they will spin (with ACQUIRE
+                * semantics) until the lock is available without waiting in
+                * the queue.
                 */
-               cnts = smp_load_acquire((u32 *)&lock->cnts);
                rspin_until_writer_unlock(lock, cnts);
                return;
        }
@@ -64,42 +86,37 @@ void queue_read_lock_slowpath(struct qrwlock *lock)
        /*
         * Put the reader into the wait queue
         */
-       arch_spin_lock(&lock->lock);
+       arch_spin_lock(&lock->wait_lock);
 
        /*
-        * At the head of the wait queue now, wait until the writer state
-        * goes to 0 and then try to increment the reader count and get
-        * the lock. It is possible that an incoming writer may steal the
-        * lock in the interim, so it is necessary to check the writer byte
-        * to make sure that the write lock isn't taken.
+        * The ACQUIRE semantics of the following spinning code ensure
+        * that accesses can't leak upwards out of our subsequent critical
+        * section in the case that the lock is currently held for write.
         */
-       while (atomic_read(&lock->cnts) & _QW_WMASK)
-               cpu_relax_lowlatency();
-
-       cnts = atomic_add_return(_QR_BIAS, &lock->cnts) - _QR_BIAS;
+       cnts = atomic_add_return_acquire(_QR_BIAS, &lock->cnts) - _QR_BIAS;
        rspin_until_writer_unlock(lock, cnts);
 
        /*
         * Signal the next one in queue to become queue head
         */
-       arch_spin_unlock(&lock->lock);
+       arch_spin_unlock(&lock->wait_lock);
 }
-EXPORT_SYMBOL(queue_read_lock_slowpath);
+EXPORT_SYMBOL(queued_read_lock_slowpath);
 
 /**
- * queue_write_lock_slowpath - acquire write lock of a queue rwlock
+ * queued_write_lock_slowpath - acquire write lock of a queue rwlock
  * @lock : Pointer to queue rwlock structure
  */
-void queue_write_lock_slowpath(struct qrwlock *lock)
+void queued_write_lock_slowpath(struct qrwlock *lock)
 {
        u32 cnts;
 
        /* Put the writer into the wait queue */
-       arch_spin_lock(&lock->lock);
+       arch_spin_lock(&lock->wait_lock);
 
        /* Try to acquire the lock directly if no reader is present */
        if (!atomic_read(&lock->cnts) &&
-           (atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0))
+           (atomic_cmpxchg_acquire(&lock->cnts, 0, _QW_LOCKED) == 0))
                goto unlock;
 
        /*
@@ -107,10 +124,10 @@ void queue_write_lock_slowpath(struct qrwlock *lock)
         * or wait for a previous writer to go away.
         */
        for (;;) {
-               cnts = atomic_read(&lock->cnts);
-               if (!(cnts & _QW_WMASK) &&
-                   (atomic_cmpxchg(&lock->cnts, cnts,
-                                   cnts | _QW_WAITING) == cnts))
+               struct __qrwlock *l = (struct __qrwlock *)lock;
+
+               if (!READ_ONCE(l->wmode) &&
+                  (cmpxchg_relaxed(&l->wmode, 0, _QW_WAITING) == 0))
                        break;
 
                cpu_relax_lowlatency();
@@ -120,13 +137,13 @@ void queue_write_lock_slowpath(struct qrwlock *lock)
        for (;;) {
                cnts = atomic_read(&lock->cnts);
                if ((cnts == _QW_WAITING) &&
-                   (atomic_cmpxchg(&lock->cnts, _QW_WAITING,
-                                   _QW_LOCKED) == _QW_WAITING))
+                   (atomic_cmpxchg_acquire(&lock->cnts, _QW_WAITING,
+                                           _QW_LOCKED) == _QW_WAITING))
                        break;
 
                cpu_relax_lowlatency();
        }
 unlock:
-       arch_spin_unlock(&lock->lock);
+       arch_spin_unlock(&lock->wait_lock);
 }
-EXPORT_SYMBOL(queue_write_lock_slowpath);
+EXPORT_SYMBOL(queued_write_lock_slowpath);