aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 12:22:03 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 12:22:03 -0400
commitc8de2fa4dc2778ae3605925c127b3deac54b2b3a (patch)
tree3b123a360b3b118d0f43efdb9180d205f8d4ccd7 /lib
parent0f47c9423c0fe468d0b5b153f9b9d6e8e20707eb (diff)
parentb5f541810ea9fb98d93c0ee0e00e07a22874856f (diff)
Merge branch 'rwsem-optimizations'
Merge rwsem optimizations from Michel Lespinasse: "These patches extend Alex Shi's work (which added write lock stealing on the rwsem slow path) in order to provide rwsem write lock stealing on the fast path (that is, without taking the rwsem's wait_lock). I have unfortunately been unable to push this through -next before due to Ingo Molnar / David Howells / Peter Zijlstra being busy with other things. However, this has gotten some attention from Rik van Riel and Davidlohr Bueso who both commented that they felt this was ready for v3.10, and Ingo Molnar has said that he was OK with me pushing directly to you. So, here goes :) Davidlohr got the following test results from pgbench running on a quad-core laptop: | db_size | clients | tps-vanilla | tps-rwsem | +---------+----------+----------------+--------------+ | 160 MB | 1 | 5803 | 6906 | + 19.0% | 160 MB | 2 | 13092 | 15931 | | 160 MB | 4 | 29412 | 33021 | | 160 MB | 8 | 32448 | 34626 | | 160 MB | 16 | 32758 | 33098 | | 160 MB | 20 | 26940 | 31343 | + 16.3% | 160 MB | 30 | 25147 | 28961 | | 160 MB | 40 | 25484 | 26902 | | 160 MB | 50 | 24528 | 25760 | ------------------------------------------------------ | 1.6 GB | 1 | 5733 | 7729 | + 34.8% | 1.6 GB | 2 | 9411 | 19009 | + 101.9% | 1.6 GB | 4 | 31818 | 33185 | | 1.6 GB | 8 | 33700 | 34550 | | 1.6 GB | 16 | 32751 | 33079 | | 1.6 GB | 20 | 30919 | 31494 | | 1.6 GB | 30 | 28540 | 28535 | | 1.6 GB | 40 | 26380 | 27054 | | 1.6 GB | 50 | 25241 | 25591 | ------------------------------------------------------ | 7.6 GB | 1 | 5779 | 6224 | | 7.6 GB | 2 | 10897 | 13611 | + 24.9% | 7.6 GB | 4 | 32683 | 33108 | | 7.6 GB | 8 | 33968 | 34712 | | 7.6 GB | 16 | 32287 | 32895 | | 7.6 GB | 20 | 27770 | 31689 | + 14.1% | 7.6 GB | 30 | 26739 | 29003 | | 7.6 GB | 40 | 24901 | 26683 | | 7.6 GB | 50 | 17115 | 25925 | + 51.5% ------------------------------------------------------ (Davidlohr also has one additional patch which further improves throughput, though I will ask him to send it directly to you as I have suggested some minor changes)." * emailed patches from Michel Lespinasse <walken@google.com>: rwsem: no need for explicit signed longs x86 rwsem: avoid taking slow path when stealing write lock rwsem: do not block readers at head of queue if other readers are active rwsem: implement support for write lock stealing on the fastpath rwsem: simplify __rwsem_do_wake rwsem: skip initial trylock in rwsem_down_write_failed rwsem: avoid taking wait_lock in rwsem_down_write_failed rwsem: use cmpxchg for trying to steal write lock rwsem: more agressive lock stealing in rwsem_down_write_failed rwsem: simplify rwsem_down_write_failed rwsem: simplify rwsem_down_read_failed rwsem: move rwsem_down_failed_common code into rwsem_down_{read,write}_failed rwsem: shorter spinlocked section in rwsem_down_failed_common() rwsem: make the waiter type an enumeration rather than a bitmask
Diffstat (limited to 'lib')
-rw-r--r--lib/rwsem-spinlock.c38
-rw-r--r--lib/rwsem.c240
2 files changed, 132 insertions, 146 deletions
diff --git a/lib/rwsem-spinlock.c b/lib/rwsem-spinlock.c
index 7542afbb22b3..9be8a9144978 100644
--- a/lib/rwsem-spinlock.c
+++ b/lib/rwsem-spinlock.c
@@ -9,12 +9,15 @@
9#include <linux/sched.h> 9#include <linux/sched.h>
10#include <linux/export.h> 10#include <linux/export.h>
11 11
12enum rwsem_waiter_type {
13 RWSEM_WAITING_FOR_WRITE,
14 RWSEM_WAITING_FOR_READ
15};
16
12struct rwsem_waiter { 17struct rwsem_waiter {
13 struct list_head list; 18 struct list_head list;
14 struct task_struct *task; 19 struct task_struct *task;
15 unsigned int flags; 20 enum rwsem_waiter_type type;
16#define RWSEM_WAITING_FOR_READ 0x00000001
17#define RWSEM_WAITING_FOR_WRITE 0x00000002
18}; 21};
19 22
20int rwsem_is_locked(struct rw_semaphore *sem) 23int rwsem_is_locked(struct rw_semaphore *sem)
@@ -67,26 +70,17 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite)
67 70
68 waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); 71 waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
69 72
70 if (!wakewrite) { 73 if (waiter->type == RWSEM_WAITING_FOR_WRITE) {
71 if (waiter->flags & RWSEM_WAITING_FOR_WRITE) 74 if (wakewrite)
72 goto out; 75 /* Wake up a writer. Note that we do not grant it the
73 goto dont_wake_writers; 76 * lock - it will have to acquire it when it runs. */
74 } 77 wake_up_process(waiter->task);
75
76 /*
77 * as we support write lock stealing, we can't set sem->activity
78 * to -1 here to indicate we get the lock. Instead, we wake it up
79 * to let it go get it again.
80 */
81 if (waiter->flags & RWSEM_WAITING_FOR_WRITE) {
82 wake_up_process(waiter->task);
83 goto out; 78 goto out;
84 } 79 }
85 80
86 /* grant an infinite number of read locks to the front of the queue */ 81 /* grant an infinite number of read locks to the front of the queue */
87 dont_wake_writers:
88 woken = 0; 82 woken = 0;
89 while (waiter->flags & RWSEM_WAITING_FOR_READ) { 83 do {
90 struct list_head *next = waiter->list.next; 84 struct list_head *next = waiter->list.next;
91 85
92 list_del(&waiter->list); 86 list_del(&waiter->list);
@@ -96,10 +90,10 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite)
96 wake_up_process(tsk); 90 wake_up_process(tsk);
97 put_task_struct(tsk); 91 put_task_struct(tsk);
98 woken++; 92 woken++;
99 if (list_empty(&sem->wait_list)) 93 if (next == &sem->wait_list)
100 break; 94 break;
101 waiter = list_entry(next, struct rwsem_waiter, list); 95 waiter = list_entry(next, struct rwsem_waiter, list);
102 } 96 } while (waiter->type != RWSEM_WAITING_FOR_WRITE);
103 97
104 sem->activity += woken; 98 sem->activity += woken;
105 99
@@ -144,7 +138,7 @@ void __sched __down_read(struct rw_semaphore *sem)
144 138
145 /* set up my own style of waitqueue */ 139 /* set up my own style of waitqueue */
146 waiter.task = tsk; 140 waiter.task = tsk;
147 waiter.flags = RWSEM_WAITING_FOR_READ; 141 waiter.type = RWSEM_WAITING_FOR_READ;
148 get_task_struct(tsk); 142 get_task_struct(tsk);
149 143
150 list_add_tail(&waiter.list, &sem->wait_list); 144 list_add_tail(&waiter.list, &sem->wait_list);
@@ -201,7 +195,7 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass)
201 /* set up my own style of waitqueue */ 195 /* set up my own style of waitqueue */
202 tsk = current; 196 tsk = current;
203 waiter.task = tsk; 197 waiter.task = tsk;
204 waiter.flags = RWSEM_WAITING_FOR_WRITE; 198 waiter.type = RWSEM_WAITING_FOR_WRITE;
205 list_add_tail(&waiter.list, &sem->wait_list); 199 list_add_tail(&waiter.list, &sem->wait_list);
206 200
207 /* wait for someone to release the lock */ 201 /* wait for someone to release the lock */
diff --git a/lib/rwsem.c b/lib/rwsem.c
index ad5e0df16ab4..cf0ad2ad19f5 100644
--- a/lib/rwsem.c
+++ b/lib/rwsem.c
@@ -4,6 +4,7 @@
4 * Derived from arch/i386/kernel/semaphore.c 4 * Derived from arch/i386/kernel/semaphore.c
5 * 5 *
6 * Writer lock-stealing by Alex Shi <alex.shi@intel.com> 6 * Writer lock-stealing by Alex Shi <alex.shi@intel.com>
7 * and Michel Lespinasse <walken@google.com>
7 */ 8 */
8#include <linux/rwsem.h> 9#include <linux/rwsem.h>
9#include <linux/sched.h> 10#include <linux/sched.h>
@@ -30,21 +31,22 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,
30 31
31EXPORT_SYMBOL(__init_rwsem); 32EXPORT_SYMBOL(__init_rwsem);
32 33
34enum rwsem_waiter_type {
35 RWSEM_WAITING_FOR_WRITE,
36 RWSEM_WAITING_FOR_READ
37};
38
33struct rwsem_waiter { 39struct rwsem_waiter {
34 struct list_head list; 40 struct list_head list;
35 struct task_struct *task; 41 struct task_struct *task;
36 unsigned int flags; 42 enum rwsem_waiter_type type;
37#define RWSEM_WAITING_FOR_READ 0x00000001
38#define RWSEM_WAITING_FOR_WRITE 0x00000002
39}; 43};
40 44
41/* Wake types for __rwsem_do_wake(). Note that RWSEM_WAKE_NO_ACTIVE and 45enum rwsem_wake_type {
42 * RWSEM_WAKE_READ_OWNED imply that the spinlock must have been kept held 46 RWSEM_WAKE_ANY, /* Wake whatever's at head of wait list */
43 * since the rwsem value was observed. 47 RWSEM_WAKE_READERS, /* Wake readers only */
44 */ 48 RWSEM_WAKE_READ_OWNED /* Waker thread holds the read lock */
45#define RWSEM_WAKE_ANY 0 /* Wake whatever's at head of wait list */ 49};
46#define RWSEM_WAKE_NO_ACTIVE 1 /* rwsem was observed with no active thread */
47#define RWSEM_WAKE_READ_OWNED 2 /* rwsem was observed to be read owned */
48 50
49/* 51/*
50 * handle the lock release when processes blocked on it that can now run 52 * handle the lock release when processes blocked on it that can now run
@@ -57,46 +59,43 @@ struct rwsem_waiter {
57 * - writers are only woken if downgrading is false 59 * - writers are only woken if downgrading is false
58 */ 60 */
59static struct rw_semaphore * 61static struct rw_semaphore *
60__rwsem_do_wake(struct rw_semaphore *sem, int wake_type) 62__rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)
61{ 63{
62 struct rwsem_waiter *waiter; 64 struct rwsem_waiter *waiter;
63 struct task_struct *tsk; 65 struct task_struct *tsk;
64 struct list_head *next; 66 struct list_head *next;
65 signed long woken, loop, adjustment; 67 long oldcount, woken, loop, adjustment;
66 68
67 waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); 69 waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
68 if (!(waiter->flags & RWSEM_WAITING_FOR_WRITE)) 70 if (waiter->type == RWSEM_WAITING_FOR_WRITE) {
69 goto readers_only; 71 if (wake_type == RWSEM_WAKE_ANY)
70 72 /* Wake writer at the front of the queue, but do not
71 if (wake_type == RWSEM_WAKE_READ_OWNED) 73 * grant it the lock yet as we want other writers
72 /* Another active reader was observed, so wakeup is not 74 * to be able to steal it. Readers, on the other hand,
73 * likely to succeed. Save the atomic op. 75 * will block as they will notice the queued writer.
74 */ 76 */
77 wake_up_process(waiter->task);
75 goto out; 78 goto out;
79 }
76 80
77 /* Wake up the writing waiter and let the task grab the sem: */ 81 /* Writers might steal the lock before we grant it to the next reader.
78 wake_up_process(waiter->task); 82 * We prefer to do the first reader grant before counting readers
79 goto out; 83 * so we can bail out early if a writer stole the lock.