dm: avoid inline filenames
[linux-block.git] / drivers / md / dm-bio-prison-v1.c
CommitLineData
3bd94003 1// SPDX-License-Identifier: GPL-2.0-only
4f81a417
MS
2/*
3 * Copyright (C) 2012 Red Hat, Inc.
4 *
5 * This file is released under the GPL.
6 */
7
8#include "dm.h"
742c8fdc
JT
9#include "dm-bio-prison-v1.h"
10#include "dm-bio-prison-v2.h"
4f81a417
MS
11
12#include <linux/spinlock.h>
13#include <linux/mempool.h>
14#include <linux/module.h>
15#include <linux/slab.h>
16
17/*----------------------------------------------------------------*/
18
a195db2d 19#define MIN_CELLS 1024
adcc4447
HM
20
21struct dm_bio_prison {
a195db2d 22 spinlock_t lock;
a195db2d 23 struct rb_root cells;
72d711c8 24 mempool_t cell_pool;
4f81a417
MS
25};
26
4f81a417
MS
27static struct kmem_cache *_cell_cache;
28
a195db2d 29/*----------------------------------------------------------------*/
adcc4447 30
4f81a417
MS
31/*
32 * @nr_cells should be the number of cells you want in use _concurrently_.
33 * Don't confuse it with the number of distinct keys.
34 */
a195db2d 35struct dm_bio_prison *dm_bio_prison_create(void)
4f81a417 36{
d3775354 37 struct dm_bio_prison *prison = kzalloc(sizeof(*prison), GFP_KERNEL);
6f1c819c 38 int ret;
4f81a417
MS
39
40 if (!prison)
41 return NULL;
42
a195db2d
JT
43 spin_lock_init(&prison->lock);
44
6f1c819c
KO
45 ret = mempool_init_slab_pool(&prison->cell_pool, MIN_CELLS, _cell_cache);
46 if (ret) {
4f81a417
MS
47 kfree(prison);
48 return NULL;
49 }
50
a195db2d 51 prison->cells = RB_ROOT;
4f81a417
MS
52
53 return prison;
54}
55EXPORT_SYMBOL_GPL(dm_bio_prison_create);
56
57void dm_bio_prison_destroy(struct dm_bio_prison *prison)
58{
6f1c819c 59 mempool_exit(&prison->cell_pool);
4f81a417
MS
60 kfree(prison);
61}
62EXPORT_SYMBOL_GPL(dm_bio_prison_destroy);
63
6beca5eb
JT
64struct dm_bio_prison_cell *dm_bio_prison_alloc_cell(struct dm_bio_prison *prison, gfp_t gfp)
65{
6f1c819c 66 return mempool_alloc(&prison->cell_pool, gfp);
6beca5eb
JT
67}
68EXPORT_SYMBOL_GPL(dm_bio_prison_alloc_cell);
69
70void dm_bio_prison_free_cell(struct dm_bio_prison *prison,
71 struct dm_bio_prison_cell *cell)
72{
6f1c819c 73 mempool_free(cell, &prison->cell_pool);
6beca5eb
JT
74}
75EXPORT_SYMBOL_GPL(dm_bio_prison_free_cell);
76
a195db2d
JT
77static void __setup_new_cell(struct dm_cell_key *key,
78 struct bio *holder,
79 struct dm_bio_prison_cell *cell)
4f81a417 80{
8ca817c4
HM
81 memcpy(&cell->key, key, sizeof(cell->key));
82 cell->holder = holder;
83 bio_list_init(&cell->bios);
4f81a417
MS
84}
85
a195db2d
JT
86static int cmp_keys(struct dm_cell_key *lhs,
87 struct dm_cell_key *rhs)
4f81a417 88{
a195db2d
JT
89 if (lhs->virtual < rhs->virtual)
90 return -1;
4f81a417 91
a195db2d
JT
92 if (lhs->virtual > rhs->virtual)
93 return 1;
adcc4447 94
a195db2d
JT
95 if (lhs->dev < rhs->dev)
96 return -1;
4f81a417 97
a195db2d
JT
98 if (lhs->dev > rhs->dev)
99 return 1;
4f81a417 100
5f274d88 101 if (lhs->block_end <= rhs->block_begin)
a195db2d 102 return -1;
4f81a417 103
5f274d88 104 if (lhs->block_begin >= rhs->block_end)
a195db2d
JT
105 return 1;
106
107 return 0;
6beca5eb 108}
4f81a417 109
a195db2d 110static int __bio_detain(struct dm_bio_prison *prison,
6beca5eb
JT
111 struct dm_cell_key *key,
112 struct bio *inmate,
113 struct dm_bio_prison_cell *cell_prealloc,
114 struct dm_bio_prison_cell **cell_result)
115{
a195db2d
JT
116 int r;
117 struct rb_node **new = &prison->cells.rb_node, *parent = NULL;
118
119 while (*new) {
120 struct dm_bio_prison_cell *cell =
6e333d0b 121 rb_entry(*new, struct dm_bio_prison_cell, node);
a195db2d
JT
122
123 r = cmp_keys(key, &cell->key);
124
125 parent = *new;
126 if (r < 0)
127 new = &((*new)->rb_left);
128 else if (r > 0)
129 new = &((*new)->rb_right);
130 else {
131 if (inmate)
132 bio_list_add(&cell->bios, inmate);
133 *cell_result = cell;
134 return 1;
135 }
4f81a417
MS
136 }
137
a195db2d 138 __setup_new_cell(key, inmate, cell_prealloc);
6beca5eb 139 *cell_result = cell_prealloc;
a195db2d
JT
140
141 rb_link_node(&cell_prealloc->node, parent, new);
142 rb_insert_color(&cell_prealloc->node, &prison->cells);
143
6beca5eb
JT
144 return 0;
145}
4f81a417 146
6beca5eb
JT
147static int bio_detain(struct dm_bio_prison *prison,
148 struct dm_cell_key *key,
149 struct bio *inmate,
150 struct dm_bio_prison_cell *cell_prealloc,
151 struct dm_bio_prison_cell **cell_result)
152{
153 int r;
4f81a417 154
235bc861 155 spin_lock_irq(&prison->lock);
a195db2d 156 r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result);
235bc861 157 spin_unlock_irq(&prison->lock);
4f81a417 158
4f81a417
MS
159 return r;
160}
6beca5eb
JT
161
162int dm_bio_detain(struct dm_bio_prison *prison,
163 struct dm_cell_key *key,
164 struct bio *inmate,
165 struct dm_bio_prison_cell *cell_prealloc,
166 struct dm_bio_prison_cell **cell_result)
167{
168 return bio_detain(prison, key, inmate, cell_prealloc, cell_result);
169}
4f81a417
MS
170EXPORT_SYMBOL_GPL(dm_bio_detain);
171
c6b4fcba
JT
172int dm_get_cell(struct dm_bio_prison *prison,
173 struct dm_cell_key *key,
174 struct dm_bio_prison_cell *cell_prealloc,
175 struct dm_bio_prison_cell **cell_result)
176{
177 return bio_detain(prison, key, NULL, cell_prealloc, cell_result);
178}
179EXPORT_SYMBOL_GPL(dm_get_cell);
180
4f81a417
MS
181/*
182 * @inmates must have been initialised prior to this call
183 */
a195db2d
JT
184static void __cell_release(struct dm_bio_prison *prison,
185 struct dm_bio_prison_cell *cell,
6beca5eb 186 struct bio_list *inmates)
4f81a417 187{
a195db2d 188 rb_erase(&cell->node, &prison->cells);
4f81a417
MS
189
190 if (inmates) {
6beca5eb
JT
191 if (cell->holder)
192 bio_list_add(inmates, cell->holder);
4f81a417
MS
193 bio_list_merge(inmates, &cell->bios);
194 }
4f81a417
MS
195}
196
6beca5eb
JT
197void dm_cell_release(struct dm_bio_prison *prison,
198 struct dm_bio_prison_cell *cell,
199 struct bio_list *bios)
4f81a417 200{
235bc861 201 spin_lock_irq(&prison->lock);
a195db2d 202 __cell_release(prison, cell, bios);
235bc861 203 spin_unlock_irq(&prison->lock);
4f81a417
MS
204}
205EXPORT_SYMBOL_GPL(dm_cell_release);
206
4f81a417
MS
207/*
208 * Sometimes we don't want the holder, just the additional bios.
209 */
a195db2d
JT
210static void __cell_release_no_holder(struct dm_bio_prison *prison,
211 struct dm_bio_prison_cell *cell,
6beca5eb 212 struct bio_list *inmates)
4f81a417 213{
a195db2d 214 rb_erase(&cell->node, &prison->cells);
4f81a417 215 bio_list_merge(inmates, &cell->bios);
4f81a417
MS
216}
217
6beca5eb
JT
218void dm_cell_release_no_holder(struct dm_bio_prison *prison,
219 struct dm_bio_prison_cell *cell,
220 struct bio_list *inmates)
4f81a417
MS
221{
222 unsigned long flags;
4f81a417 223
a195db2d
JT
224 spin_lock_irqsave(&prison->lock, flags);
225 __cell_release_no_holder(prison, cell, inmates);
226 spin_unlock_irqrestore(&prison->lock, flags);
4f81a417
MS
227}
228EXPORT_SYMBOL_GPL(dm_cell_release_no_holder);
229
6beca5eb 230void dm_cell_error(struct dm_bio_prison *prison,
4e4cbee9 231 struct dm_bio_prison_cell *cell, blk_status_t error)
4f81a417 232{
4f81a417
MS
233 struct bio_list bios;
234 struct bio *bio;
4f81a417
MS
235
236 bio_list_init(&bios);
adcc4447 237 dm_cell_release(prison, cell, &bios);
4f81a417 238
4246a0b6 239 while ((bio = bio_list_pop(&bios))) {
4e4cbee9 240 bio->bi_status = error;
4246a0b6
CH
241 bio_endio(bio);
242 }
4f81a417
MS
243}
244EXPORT_SYMBOL_GPL(dm_cell_error);
245
2d759a46
JT
246void dm_cell_visit_release(struct dm_bio_prison *prison,
247 void (*visit_fn)(void *, struct dm_bio_prison_cell *),
248 void *context,
249 struct dm_bio_prison_cell *cell)
250{
235bc861 251 spin_lock_irq(&prison->lock);
2d759a46
JT
252 visit_fn(context, cell);
253 rb_erase(&cell->node, &prison->cells);
235bc861 254 spin_unlock_irq(&prison->lock);
2d759a46
JT
255}
256EXPORT_SYMBOL_GPL(dm_cell_visit_release);
257
3cdf93f9
JT
258static int __promote_or_release(struct dm_bio_prison *prison,
259 struct dm_bio_prison_cell *cell)
260{
261 if (bio_list_empty(&cell->bios)) {
262 rb_erase(&cell->node, &prison->cells);
263 return 1;
264 }
265
266 cell->holder = bio_list_pop(&cell->bios);
267 return 0;
268}
269
270int dm_cell_promote_or_release(struct dm_bio_prison *prison,
271 struct dm_bio_prison_cell *cell)
272{
273 int r;
3cdf93f9 274
235bc861 275 spin_lock_irq(&prison->lock);
3cdf93f9 276 r = __promote_or_release(prison, cell);
235bc861 277 spin_unlock_irq(&prison->lock);
3cdf93f9
JT
278
279 return r;
280}
281EXPORT_SYMBOL_GPL(dm_cell_promote_or_release);
282
4f81a417
MS
283/*----------------------------------------------------------------*/
284
285#define DEFERRED_SET_SIZE 64
286
287struct dm_deferred_entry {
288 struct dm_deferred_set *ds;
86a3238c 289 unsigned int count;
4f81a417
MS
290 struct list_head work_items;
291};
292
293struct dm_deferred_set {
294 spinlock_t lock;
86a3238c
HM
295 unsigned int current_entry;
296 unsigned int sweeper;
4f81a417
MS
297 struct dm_deferred_entry entries[DEFERRED_SET_SIZE];
298};
299
300struct dm_deferred_set *dm_deferred_set_create(void)
301{
302 int i;
303 struct dm_deferred_set *ds;
304
305 ds = kmalloc(sizeof(*ds), GFP_KERNEL);
306 if (!ds)
307 return NULL;
308
309 spin_lock_init(&ds->lock);
310 ds->current_entry = 0;
311 ds->sweeper = 0;
312 for (i = 0; i < DEFERRED_SET_SIZE; i++) {
313 ds->entries[i].ds = ds;
314 ds->entries[i].count = 0;
315 INIT_LIST_HEAD(&ds->entries[i].work_items);
316 }
317
318 return ds;
319}
320EXPORT_SYMBOL_GPL(dm_deferred_set_create);
321
322void dm_deferred_set_destroy(struct dm_deferred_set *ds)
323{
324 kfree(ds);
325}
326EXPORT_SYMBOL_GPL(dm_deferred_set_destroy);
327
328struct dm_deferred_entry *dm_deferred_entry_inc(struct dm_deferred_set *ds)
329{
330 unsigned long flags;
331 struct dm_deferred_entry *entry;
332
333 spin_lock_irqsave(&ds->lock, flags);
334 entry = ds->entries + ds->current_entry;
335 entry->count++;
336 spin_unlock_irqrestore(&ds->lock, flags);
337
338 return entry;
339}
340EXPORT_SYMBOL_GPL(dm_deferred_entry_inc);
341
86a3238c 342static unsigned int ds_next(unsigned int index)
4f81a417
MS
343{
344 return (index + 1) % DEFERRED_SET_SIZE;
345}
346
347static void __sweep(struct dm_deferred_set *ds, struct list_head *head)
348{
349 while ((ds->sweeper != ds->current_entry) &&
350 !ds->entries[ds->sweeper].count) {
351 list_splice_init(&ds->entries[ds->sweeper].work_items, head);
352 ds->sweeper = ds_next(ds->sweeper);
353 }
354
355 if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count)
356 list_splice_init(&ds->entries[ds->sweeper].work_items, head);
357}
358
359void dm_deferred_entry_dec(struct dm_deferred_entry *entry, struct list_head *head)
360{
361 unsigned long flags;
362
363 spin_lock_irqsave(&entry->ds->lock, flags);
364 BUG_ON(!entry->count);
365 --entry->count;
366 __sweep(entry->ds, head);
367 spin_unlock_irqrestore(&entry->ds->lock, flags);
368}
369EXPORT_SYMBOL_GPL(dm_deferred_entry_dec);
370
371/*
372 * Returns 1 if deferred or 0 if no pending items to delay job.
373 */
374int dm_deferred_set_add_work(struct dm_deferred_set *ds, struct list_head *work)
375{
376 int r = 1;
86a3238c 377 unsigned int next_entry;
4f81a417 378
235bc861 379 spin_lock_irq(&ds->lock);
4f81a417
MS
380 if ((ds->sweeper == ds->current_entry) &&
381 !ds->entries[ds->current_entry].count)
382 r = 0;
383 else {
384 list_add(work, &ds->entries[ds->current_entry].work_items);
385 next_entry = ds_next(ds->current_entry);
386 if (!ds->entries[next_entry].count)
387 ds->current_entry = next_entry;
388 }
235bc861 389 spin_unlock_irq(&ds->lock);
4f81a417
MS
390
391 return r;
392}
393EXPORT_SYMBOL_GPL(dm_deferred_set_add_work);
394
395/*----------------------------------------------------------------*/
396
742c8fdc 397static int __init dm_bio_prison_init_v1(void)
4f81a417
MS
398{
399 _cell_cache = KMEM_CACHE(dm_bio_prison_cell, 0);
400 if (!_cell_cache)
401 return -ENOMEM;
402
403 return 0;
404}
405
742c8fdc 406static void dm_bio_prison_exit_v1(void)
4f81a417
MS
407{
408 kmem_cache_destroy(_cell_cache);
409 _cell_cache = NULL;
410}
411
742c8fdc
JT
412static int (*_inits[])(void) __initdata = {
413 dm_bio_prison_init_v1,
414 dm_bio_prison_init_v2,
415};
416
417static void (*_exits[])(void) = {
418 dm_bio_prison_exit_v1,
419 dm_bio_prison_exit_v2,
420};
421
422static int __init dm_bio_prison_init(void)
423{
424 const int count = ARRAY_SIZE(_inits);
425
426 int r, i;
427
428 for (i = 0; i < count; i++) {
429 r = _inits[i]();
430 if (r)
431 goto bad;
432 }
433
434 return 0;
435
436 bad:
437 while (i--)
438 _exits[i]();
439
440 return r;
441}
442
443static void __exit dm_bio_prison_exit(void)
444{
445 int i = ARRAY_SIZE(_exits);
446
447 while (i--)
448 _exits[i]();
449}
450
4f81a417
MS
451/*
452 * module hooks
453 */
454module_init(dm_bio_prison_init);
455module_exit(dm_bio_prison_exit);
456
457MODULE_DESCRIPTION(DM_NAME " bio prison");
458MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
459MODULE_LICENSE("GPL");