Merge tag 'arm-dt-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-2.6-microblaze.git] / kernel / locking / rwsem.c
index 9d1db4a..65f0262 100644 (file)
@@ -335,8 +335,6 @@ struct rwsem_waiter {
        struct task_struct *task;
        enum rwsem_waiter_type type;
        unsigned long timeout;
-
-       /* Writer only, not initialized in reader */
        bool handoff_set;
 };
 #define rwsem_first_waiter(sem) \
@@ -459,10 +457,12 @@ static void rwsem_mark_wake(struct rw_semaphore *sem,
                         * to give up the lock), request a HANDOFF to
                         * force the issue.
                         */
-                       if (!(oldcount & RWSEM_FLAG_HANDOFF) &&
-                           time_after(jiffies, waiter->timeout)) {
-                               adjustment -= RWSEM_FLAG_HANDOFF;
-                               lockevent_inc(rwsem_rlock_handoff);
+                       if (time_after(jiffies, waiter->timeout)) {
+                               if (!(oldcount & RWSEM_FLAG_HANDOFF)) {
+                                       adjustment -= RWSEM_FLAG_HANDOFF;
+                                       lockevent_inc(rwsem_rlock_handoff);
+                               }
+                               waiter->handoff_set = true;
                        }
 
                        atomic_long_add(-adjustment, &sem->count);
@@ -599,7 +599,7 @@ rwsem_del_wake_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter,
 static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
                                        struct rwsem_waiter *waiter)
 {
-       bool first = rwsem_first_waiter(sem) == waiter;
+       struct rwsem_waiter *first = rwsem_first_waiter(sem);
        long count, new;
 
        lockdep_assert_held(&sem->wait_lock);
@@ -609,11 +609,20 @@ static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
                bool has_handoff = !!(count & RWSEM_FLAG_HANDOFF);
 
                if (has_handoff) {
-                       if (!first)
+                       /*
+                        * Honor handoff bit and yield only when the first
+                        * waiter is the one that set it. Otherwisee, we
+                        * still try to acquire the rwsem.
+                        */
+                       if (first->handoff_set && (waiter != first))
                                return false;
 
-                       /* First waiter inherits a previously set handoff bit */
-                       waiter->handoff_set = true;
+                       /*
+                        * First waiter can inherit a previously set handoff
+                        * bit and spin on rwsem if lock acquisition fails.
+                        */
+                       if (waiter == first)
+                               waiter->handoff_set = true;
                }
 
                new = count;
@@ -1027,6 +1036,7 @@ queue:
        waiter.task = current;
        waiter.type = RWSEM_WAITING_FOR_READ;
        waiter.timeout = jiffies + RWSEM_WAIT_TIMEOUT;
+       waiter.handoff_set = false;
 
        raw_spin_lock_irq(&sem->wait_lock);
        if (list_empty(&sem->wait_list)) {