diff options
author | Heinz Mauelshagen <heinzm@redhat.com> | 2014-06-05 10:23:09 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2014-06-11 16:48:54 -0400 |
commit | adcc44472bacb227ebc0b1a8876efa5302474338 (patch) | |
tree | 594f3024476830cb98887266b282307cc960dd3d /drivers/md | |
parent | 11f0431be2f99c574a65c6dfc0ca205511500f29 (diff) |
dm bio prison: implement per bucket locking in the dm_bio_prison hash table
Split the single per bio-prison lock by using per bucket locking. Per
bucket locking benefits both dm-thin and dm-cache targets by reducing
bio-prison lock contention.
Signed-off-by: Heinz Mauelshagen <heinzm@redhat.com>
Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-bio-prison.c | 66 |
1 files changed, 39 insertions, 27 deletions
diff --git a/drivers/md/dm-bio-prison.c b/drivers/md/dm-bio-prison.c index 71434382cb64..f752d12081ff 100644 --- a/drivers/md/dm-bio-prison.c +++ b/drivers/md/dm-bio-prison.c | |||
@@ -14,13 +14,17 @@ | |||
14 | 14 | ||
15 | /*----------------------------------------------------------------*/ | 15 | /*----------------------------------------------------------------*/ |
16 | 16 | ||
17 | struct dm_bio_prison { | 17 | struct bucket { |
18 | spinlock_t lock; | 18 | spinlock_t lock; |
19 | struct hlist_head cells; | ||
20 | }; | ||
21 | |||
22 | struct dm_bio_prison { | ||
19 | mempool_t *cell_pool; | 23 | mempool_t *cell_pool; |
20 | 24 | ||
21 | unsigned nr_buckets; | 25 | unsigned nr_buckets; |
22 | unsigned hash_mask; | 26 | unsigned hash_mask; |
23 | struct hlist_head *cells; | 27 | struct bucket *buckets; |
24 | }; | 28 | }; |
25 | 29 | ||
26 | /*----------------------------------------------------------------*/ | 30 | /*----------------------------------------------------------------*/ |
@@ -40,6 +44,12 @@ static uint32_t calc_nr_buckets(unsigned nr_cells) | |||
40 | 44 | ||
41 | static struct kmem_cache *_cell_cache; | 45 | static struct kmem_cache *_cell_cache; |
42 | 46 | ||
47 | static void init_bucket(struct bucket *b) | ||
48 | { | ||
49 | spin_lock_init(&b->lock); | ||
50 | INIT_HLIST_HEAD(&b->cells); | ||
51 | } | ||
52 | |||
43 | /* | 53 | /* |
44 | * @nr_cells should be the number of cells you want in use _concurrently_. | 54 | * @nr_cells should be the number of cells you want in use _concurrently_. |
45 | * Don't confuse it with the number of distinct keys. | 55 | * Don't confuse it with the number of distinct keys. |
@@ -49,13 +59,12 @@ struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells) | |||
49 | unsigned i; | 59 | unsigned i; |
50 | uint32_t nr_buckets = calc_nr_buckets(nr_cells); | 60 | uint32_t nr_buckets = calc_nr_buckets(nr_cells); |
51 | size_t len = sizeof(struct dm_bio_prison) + | 61 | size_t len = sizeof(struct dm_bio_prison) + |
52 | (sizeof(struct hlist_head) * nr_buckets); | 62 | (sizeof(struct bucket) * nr_buckets); |
53 | struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL); | 63 | struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL); |
54 | 64 | ||
55 | if (!prison) | 65 | if (!prison) |
56 | return NULL; | 66 | return NULL; |
57 | 67 | ||
58 | spin_lock_init(&prison->lock); | ||
59 | prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache); | 68 | prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache); |
60 | if (!prison->cell_pool) { | 69 | if (!prison->cell_pool) { |
61 | kfree(prison); | 70 | kfree(prison); |
@@ -64,9 +73,9 @@ struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells) | |||
64 | 73 | ||
65 | prison->nr_buckets = nr_buckets; | 74 | prison->nr_buckets = nr_buckets; |
66 | prison->hash_mask = nr_buckets - 1; | 75 | prison->hash_mask = nr_buckets - 1; |
67 | prison->cells = (struct hlist_head *) (prison + 1); | 76 | prison->buckets = (struct bucket *) (prison + 1); |
68 | for (i = 0; i < nr_buckets; i++) | 77 | for (i = 0; i < nr_buckets; i++) |
69 | INIT_HLIST_HEAD(prison->cells + i); | 78 | init_bucket(prison->buckets + i); |
70 | 79 | ||
71 | return prison; | 80 | return prison; |
72 | } | 81 | } |
@@ -107,40 +116,44 @@ static int keys_equal(struct dm_cell_key *lhs, struct dm_cell_key *rhs) | |||
107 | (lhs->block == rhs->block); | 116 | (lhs->block == rhs->block); |
108 | } | 117 | } |
109 | 118 | ||
110 | static struct dm_bio_prison_cell *__search_bucket(struct hlist_head *bucket, | 119 | static struct bucket *get_bucket(struct dm_bio_prison *prison, |
120 | struct dm_cell_key *key) | ||
121 | { | ||
122 | return prison->buckets + hash_key(prison, key); | ||
123 | } | ||
124 | |||
125 | static struct dm_bio_prison_cell *__search_bucket(struct bucket *b, | ||
111 | struct dm_cell_key *key) | 126 | struct dm_cell_key *key) |
112 | { | 127 | { |
113 | struct dm_bio_prison_cell *cell; | 128 | struct dm_bio_prison_cell *cell; |
114 | 129 | ||
115 | hlist_for_each_entry(cell, bucket, list) | 130 | hlist_for_each_entry(cell, &b->cells, list) |
116 | if (keys_equal(&cell->key, key)) | 131 | if (keys_equal(&cell->key, key)) |
117 | return cell; | 132 | return cell; |
118 | 133 | ||
119 | return NULL; | 134 | return NULL; |
120 | } | 135 | } |
121 | 136 | ||
122 | static void __setup_new_cell(struct dm_bio_prison *prison, | 137 | static void __setup_new_cell(struct bucket *b, |
123 | struct dm_cell_key *key, | 138 | struct dm_cell_key *key, |
124 | struct bio *holder, | 139 | struct bio *holder, |
125 | uint32_t hash, | ||
126 | struct dm_bio_prison_cell *cell) | 140 | struct dm_bio_prison_cell *cell) |
127 | { | 141 | { |
128 | memcpy(&cell->key, key, sizeof(cell->key)); | 142 | memcpy(&cell->key, key, sizeof(cell->key)); |
129 | cell->holder = holder; | 143 | cell->holder = holder; |
130 | bio_list_init(&cell->bios); | 144 | bio_list_init(&cell->bios); |
131 | hlist_add_head(&cell->list, prison->cells + hash); | 145 | hlist_add_head(&cell->list, &b->cells); |
132 | } | 146 | } |
133 | 147 | ||
134 | static int __bio_detain(struct dm_bio_prison *prison, | 148 | static int __bio_detain(struct bucket *b, |
135 | struct dm_cell_key *key, | 149 | struct dm_cell_key *key, |
136 | struct bio *inmate, | 150 | struct bio *inmate, |
137 | struct dm_bio_prison_cell *cell_prealloc, | 151 | struct dm_bio_prison_cell *cell_prealloc, |
138 | struct dm_bio_prison_cell **cell_result) | 152 | struct dm_bio_prison_cell **cell_result) |
139 | { | 153 | { |
140 | uint32_t hash = hash_key(prison, key); | ||
141 | struct dm_bio_prison_cell *cell; | 154 | struct dm_bio_prison_cell *cell; |
142 | 155 | ||
143 | cell = __search_bucket(prison->cells + hash, key); | 156 | cell = __search_bucket(b, key); |
144 | if (cell) { | 157 | if (cell) { |
145 | if (inmate) | 158 | if (inmate) |
146 | bio_list_add(&cell->bios, inmate); | 159 | bio_list_add(&cell->bios, inmate); |
@@ -148,7 +161,7 @@ static int __bio_detain(struct dm_bio_prison *prison, | |||
148 | return 1; | 161 | return 1; |
149 | } | 162 | } |
150 | 163 | ||
151 | __setup_new_cell(prison, key, inmate, hash, cell_prealloc); | 164 | __setup_new_cell(b, key, inmate, cell_prealloc); |
152 | *cell_result = cell_prealloc; | 165 | *cell_result = cell_prealloc; |
153 | return 0; | 166 | return 0; |
154 | } | 167 | } |
@@ -161,10 +174,11 @@ static int bio_detain(struct dm_bio_prison *prison, | |||
161 | { | 174 | { |
162 | int r; | 175 | int r; |
163 | unsigned long flags; | 176 | unsigned long flags; |
177 | struct bucket *b = get_bucket(prison, key); | ||
164 | 178 | ||
165 | spin_lock_irqsave(&prison->lock, flags); | 179 | spin_lock_irqsave(&b->lock, flags); |
166 | r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result); | 180 | r = __bio_detain(b, key, inmate, cell_prealloc, cell_result); |
167 | spin_unlock_irqrestore(&prison->lock, flags); | 181 | spin_unlock_irqrestore(&b->lock, flags); |
168 | 182 | ||
169 | return r; | 183 | return r; |
170 | } | 184 | } |
@@ -208,10 +222,11 @@ void dm_cell_release(struct dm_bio_prison *prison, | |||
208 | struct bio_list *bios) | 222 | struct bio_list *bios) |
209 | { | 223 | { |
210 | unsigned long flags; | 224 | unsigned long flags; |
225 | struct bucket *b = get_bucket(prison, &cell->key); | ||
211 | 226 | ||
212 | spin_lock_irqsave(&prison->lock, flags); | 227 | spin_lock_irqsave(&b->lock, flags); |
213 | __cell_release(cell, bios); | 228 | __cell_release(cell, bios); |
214 | spin_unlock_irqrestore(&prison->lock, flags); | 229 | spin_unlock_irqrestore(&b->lock, flags); |
215 | } | 230 | } |
216 | EXPORT_SYMBOL_GPL(dm_cell_release); | 231 | EXPORT_SYMBOL_GPL(dm_cell_release); |
217 | 232 | ||
@@ -230,10 +245,11 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison, | |||
230 | struct bio_list *inmates) | 245 | struct bio_list *inmates) |
231 | { | 246 | { |
232 | unsigned long flags; | 247 | unsigned long flags; |
248 | struct bucket *b = get_bucket(prison, &cell->key); | ||
233 | 249 | ||
234 | spin_lock_irqsave(&prison->lock, flags); | 250 | spin_lock_irqsave(&b->lock, flags); |
235 | __cell_release_no_holder(cell, inmates); | 251 | __cell_release_no_holder(cell, inmates); |
236 | spin_unlock_irqrestore(&prison->lock, flags); | 252 | spin_unlock_irqrestore(&b->lock, flags); |
237 | } | 253 | } |
238 | EXPORT_SYMBOL_GPL(dm_cell_release_no_holder); | 254 | EXPORT_SYMBOL_GPL(dm_cell_release_no_holder); |
239 | 255 | ||
@@ -242,13 +258,9 @@ void dm_cell_error(struct dm_bio_prison *prison, | |||
242 | { | 258 | { |
243 | struct bio_list bios; | 259 | struct bio_list bios; |
244 | struct bio *bio; | 260 | struct bio *bio; |
245 | unsigned long flags; | ||
246 | 261 | ||
247 | bio_list_init(&bios); | 262 | bio_list_init(&bios); |
248 | 263 | dm_cell_release(prison, cell, &bios); | |
249 | spin_lock_irqsave(&prison->lock, flags); | ||
250 | __cell_release(cell, &bios); | ||
251 | spin_unlock_irqrestore(&prison->lock, flags); | ||
252 | 264 | ||
253 | while ((bio = bio_list_pop(&bios))) | 265 | while ((bio = bio_list_pop(&bios))) |
254 | bio_endio(bio, error); | 266 | bio_endio(bio, error); |