Blame view

drivers/md/dm-bio-prison-v1.c 9.98 KB
4f81a4176   Mike Snitzer   dm thin: move bio...
1
2
3
4
5
6
7
  /*
   * Copyright (C) 2012 Red Hat, Inc.
   *
   * This file is released under the GPL.
   */
  
  #include "dm.h"
742c8fdc3   Joe Thornber   dm bio prison v2:...
8
9
  #include "dm-bio-prison-v1.h"
  #include "dm-bio-prison-v2.h"
4f81a4176   Mike Snitzer   dm thin: move bio...
10
11
12
13
14
15
16
  
  #include <linux/spinlock.h>
  #include <linux/mempool.h>
  #include <linux/module.h>
  #include <linux/slab.h>
  
  /*----------------------------------------------------------------*/
a195db2d2   Joe Thornber   dm bio prison: sw...
17
  #define MIN_CELLS 1024
adcc44472   Heinz Mauelshagen   dm bio prison: im...
18
19
  
  struct dm_bio_prison {
a195db2d2   Joe Thornber   dm bio prison: sw...
20
  	spinlock_t lock;
a195db2d2   Joe Thornber   dm bio prison: sw...
21
  	struct rb_root cells;
72d711c87   Mike Snitzer   dm: adjust struct...
22
  	mempool_t cell_pool;
4f81a4176   Mike Snitzer   dm thin: move bio...
23
  };
4f81a4176   Mike Snitzer   dm thin: move bio...
24
  static struct kmem_cache *_cell_cache;
a195db2d2   Joe Thornber   dm bio prison: sw...
25
  /*----------------------------------------------------------------*/
adcc44472   Heinz Mauelshagen   dm bio prison: im...
26

4f81a4176   Mike Snitzer   dm thin: move bio...
27
28
29
30
  /*
   * @nr_cells should be the number of cells you want in use _concurrently_.
   * Don't confuse it with the number of distinct keys.
   */
a195db2d2   Joe Thornber   dm bio prison: sw...
31
  struct dm_bio_prison *dm_bio_prison_create(void)
4f81a4176   Mike Snitzer   dm thin: move bio...
32
  {
d37753540   Kent Overstreet   dm: Use kzalloc f...
33
  	struct dm_bio_prison *prison = kzalloc(sizeof(*prison), GFP_KERNEL);
6f1c819c2   Kent Overstreet   dm: convert to bi...
34
  	int ret;
4f81a4176   Mike Snitzer   dm thin: move bio...
35
36
37
  
  	if (!prison)
  		return NULL;
a195db2d2   Joe Thornber   dm bio prison: sw...
38
  	spin_lock_init(&prison->lock);
6f1c819c2   Kent Overstreet   dm: convert to bi...
39
40
  	ret = mempool_init_slab_pool(&prison->cell_pool, MIN_CELLS, _cell_cache);
  	if (ret) {
4f81a4176   Mike Snitzer   dm thin: move bio...
41
42
43
  		kfree(prison);
  		return NULL;
  	}
a195db2d2   Joe Thornber   dm bio prison: sw...
44
  	prison->cells = RB_ROOT;
4f81a4176   Mike Snitzer   dm thin: move bio...
45
46
47
48
49
50
51
  
  	return prison;
  }
  EXPORT_SYMBOL_GPL(dm_bio_prison_create);
  
  void dm_bio_prison_destroy(struct dm_bio_prison *prison)
  {
6f1c819c2   Kent Overstreet   dm: convert to bi...
52
  	mempool_exit(&prison->cell_pool);
4f81a4176   Mike Snitzer   dm thin: move bio...
53
54
55
  	kfree(prison);
  }
  EXPORT_SYMBOL_GPL(dm_bio_prison_destroy);
6beca5eb6   Joe Thornber   dm bio prison: pa...
56
57
  struct dm_bio_prison_cell *dm_bio_prison_alloc_cell(struct dm_bio_prison *prison, gfp_t gfp)
  {
6f1c819c2   Kent Overstreet   dm: convert to bi...
58
  	return mempool_alloc(&prison->cell_pool, gfp);
6beca5eb6   Joe Thornber   dm bio prison: pa...
59
60
61
62
63
64
  }
  EXPORT_SYMBOL_GPL(dm_bio_prison_alloc_cell);
  
  void dm_bio_prison_free_cell(struct dm_bio_prison *prison,
  			     struct dm_bio_prison_cell *cell)
  {
6f1c819c2   Kent Overstreet   dm: convert to bi...
65
  	mempool_free(cell, &prison->cell_pool);
6beca5eb6   Joe Thornber   dm bio prison: pa...
66
67
  }
  EXPORT_SYMBOL_GPL(dm_bio_prison_free_cell);
a195db2d2   Joe Thornber   dm bio prison: sw...
68
69
70
  static void __setup_new_cell(struct dm_cell_key *key,
  			     struct bio *holder,
  			     struct dm_bio_prison_cell *cell)
4f81a4176   Mike Snitzer   dm thin: move bio...
71
  {
a195db2d2   Joe Thornber   dm bio prison: sw...
72
73
74
         memcpy(&cell->key, key, sizeof(cell->key));
         cell->holder = holder;
         bio_list_init(&cell->bios);
4f81a4176   Mike Snitzer   dm thin: move bio...
75
  }
a195db2d2   Joe Thornber   dm bio prison: sw...
76
77
  static int cmp_keys(struct dm_cell_key *lhs,
  		    struct dm_cell_key *rhs)
4f81a4176   Mike Snitzer   dm thin: move bio...
78
  {
a195db2d2   Joe Thornber   dm bio prison: sw...
79
80
  	if (lhs->virtual < rhs->virtual)
  		return -1;
4f81a4176   Mike Snitzer   dm thin: move bio...
81

a195db2d2   Joe Thornber   dm bio prison: sw...
82
83
  	if (lhs->virtual > rhs->virtual)
  		return 1;
adcc44472   Heinz Mauelshagen   dm bio prison: im...
84

a195db2d2   Joe Thornber   dm bio prison: sw...
85
86
  	if (lhs->dev < rhs->dev)
  		return -1;
4f81a4176   Mike Snitzer   dm thin: move bio...
87

a195db2d2   Joe Thornber   dm bio prison: sw...
88
89
  	if (lhs->dev > rhs->dev)
  		return 1;
4f81a4176   Mike Snitzer   dm thin: move bio...
90

5f274d886   Joe Thornber   dm bio prison: in...
91
  	if (lhs->block_end <= rhs->block_begin)
a195db2d2   Joe Thornber   dm bio prison: sw...
92
  		return -1;
4f81a4176   Mike Snitzer   dm thin: move bio...
93

5f274d886   Joe Thornber   dm bio prison: in...
94
  	if (lhs->block_begin >= rhs->block_end)
a195db2d2   Joe Thornber   dm bio prison: sw...
95
96
97
  		return 1;
  
  	return 0;
6beca5eb6   Joe Thornber   dm bio prison: pa...
98
  }
4f81a4176   Mike Snitzer   dm thin: move bio...
99

a195db2d2   Joe Thornber   dm bio prison: sw...
100
  static int __bio_detain(struct dm_bio_prison *prison,
6beca5eb6   Joe Thornber   dm bio prison: pa...
101
102
103
104
105
  			struct dm_cell_key *key,
  			struct bio *inmate,
  			struct dm_bio_prison_cell *cell_prealloc,
  			struct dm_bio_prison_cell **cell_result)
  {
a195db2d2   Joe Thornber   dm bio prison: sw...
106
107
108
109
110
  	int r;
  	struct rb_node **new = &prison->cells.rb_node, *parent = NULL;
  
  	while (*new) {
  		struct dm_bio_prison_cell *cell =
6e333d0be   Geliang Tang   dm bio prison: us...
111
  			rb_entry(*new, struct dm_bio_prison_cell, node);
a195db2d2   Joe Thornber   dm bio prison: sw...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  
  		r = cmp_keys(key, &cell->key);
  
  		parent = *new;
  		if (r < 0)
  			new = &((*new)->rb_left);
  		else if (r > 0)
  			new = &((*new)->rb_right);
  		else {
  			if (inmate)
  				bio_list_add(&cell->bios, inmate);
  			*cell_result = cell;
  			return 1;
  		}
4f81a4176   Mike Snitzer   dm thin: move bio...
126
  	}
a195db2d2   Joe Thornber   dm bio prison: sw...
127
  	__setup_new_cell(key, inmate, cell_prealloc);
6beca5eb6   Joe Thornber   dm bio prison: pa...
128
  	*cell_result = cell_prealloc;
a195db2d2   Joe Thornber   dm bio prison: sw...
129
130
131
  
  	rb_link_node(&cell_prealloc->node, parent, new);
  	rb_insert_color(&cell_prealloc->node, &prison->cells);
6beca5eb6   Joe Thornber   dm bio prison: pa...
132
133
  	return 0;
  }
4f81a4176   Mike Snitzer   dm thin: move bio...
134

6beca5eb6   Joe Thornber   dm bio prison: pa...
135
136
137
138
139
140
141
  static int bio_detain(struct dm_bio_prison *prison,
  		      struct dm_cell_key *key,
  		      struct bio *inmate,
  		      struct dm_bio_prison_cell *cell_prealloc,
  		      struct dm_bio_prison_cell **cell_result)
  {
  	int r;
4f81a4176   Mike Snitzer   dm thin: move bio...
142

235bc8616   Mikulas Patocka   dm bio prison: re...
143
  	spin_lock_irq(&prison->lock);
a195db2d2   Joe Thornber   dm bio prison: sw...
144
  	r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result);
235bc8616   Mikulas Patocka   dm bio prison: re...
145
  	spin_unlock_irq(&prison->lock);
4f81a4176   Mike Snitzer   dm thin: move bio...
146

4f81a4176   Mike Snitzer   dm thin: move bio...
147
148
  	return r;
  }
6beca5eb6   Joe Thornber   dm bio prison: pa...
149
150
151
152
153
154
155
156
157
  
  int dm_bio_detain(struct dm_bio_prison *prison,
  		  struct dm_cell_key *key,
  		  struct bio *inmate,
  		  struct dm_bio_prison_cell *cell_prealloc,
  		  struct dm_bio_prison_cell **cell_result)
  {
  	return bio_detain(prison, key, inmate, cell_prealloc, cell_result);
  }
4f81a4176   Mike Snitzer   dm thin: move bio...
158
  EXPORT_SYMBOL_GPL(dm_bio_detain);
c6b4fcbad   Joe Thornber   dm: add cache target
159
160
161
162
163
164
165
166
  int dm_get_cell(struct dm_bio_prison *prison,
  		struct dm_cell_key *key,
  		struct dm_bio_prison_cell *cell_prealloc,
  		struct dm_bio_prison_cell **cell_result)
  {
  	return bio_detain(prison, key, NULL, cell_prealloc, cell_result);
  }
  EXPORT_SYMBOL_GPL(dm_get_cell);
4f81a4176   Mike Snitzer   dm thin: move bio...
167
168
169
  /*
   * @inmates must have been initialised prior to this call
   */
a195db2d2   Joe Thornber   dm bio prison: sw...
170
171
  static void __cell_release(struct dm_bio_prison *prison,
  			   struct dm_bio_prison_cell *cell,
6beca5eb6   Joe Thornber   dm bio prison: pa...
172
  			   struct bio_list *inmates)
4f81a4176   Mike Snitzer   dm thin: move bio...
173
  {
a195db2d2   Joe Thornber   dm bio prison: sw...
174
  	rb_erase(&cell->node, &prison->cells);
4f81a4176   Mike Snitzer   dm thin: move bio...
175
176
  
  	if (inmates) {
6beca5eb6   Joe Thornber   dm bio prison: pa...
177
178
  		if (cell->holder)
  			bio_list_add(inmates, cell->holder);
4f81a4176   Mike Snitzer   dm thin: move bio...
179
180
  		bio_list_merge(inmates, &cell->bios);
  	}
4f81a4176   Mike Snitzer   dm thin: move bio...
181
  }
6beca5eb6   Joe Thornber   dm bio prison: pa...
182
183
184
  void dm_cell_release(struct dm_bio_prison *prison,
  		     struct dm_bio_prison_cell *cell,
  		     struct bio_list *bios)
4f81a4176   Mike Snitzer   dm thin: move bio...
185
  {
235bc8616   Mikulas Patocka   dm bio prison: re...
186
  	spin_lock_irq(&prison->lock);
a195db2d2   Joe Thornber   dm bio prison: sw...
187
  	__cell_release(prison, cell, bios);
235bc8616   Mikulas Patocka   dm bio prison: re...
188
  	spin_unlock_irq(&prison->lock);
4f81a4176   Mike Snitzer   dm thin: move bio...
189
190
191
192
  }
  EXPORT_SYMBOL_GPL(dm_cell_release);
  
  /*
4f81a4176   Mike Snitzer   dm thin: move bio...
193
194
   * Sometimes we don't want the holder, just the additional bios.
   */
a195db2d2   Joe Thornber   dm bio prison: sw...
195
196
  static void __cell_release_no_holder(struct dm_bio_prison *prison,
  				     struct dm_bio_prison_cell *cell,
6beca5eb6   Joe Thornber   dm bio prison: pa...
197
  				     struct bio_list *inmates)
4f81a4176   Mike Snitzer   dm thin: move bio...
198
  {
a195db2d2   Joe Thornber   dm bio prison: sw...
199
  	rb_erase(&cell->node, &prison->cells);
4f81a4176   Mike Snitzer   dm thin: move bio...
200
  	bio_list_merge(inmates, &cell->bios);
4f81a4176   Mike Snitzer   dm thin: move bio...
201
  }
6beca5eb6   Joe Thornber   dm bio prison: pa...
202
203
204
  void dm_cell_release_no_holder(struct dm_bio_prison *prison,
  			       struct dm_bio_prison_cell *cell,
  			       struct bio_list *inmates)
4f81a4176   Mike Snitzer   dm thin: move bio...
205
206
  {
  	unsigned long flags;
4f81a4176   Mike Snitzer   dm thin: move bio...
207

a195db2d2   Joe Thornber   dm bio prison: sw...
208
209
210
  	spin_lock_irqsave(&prison->lock, flags);
  	__cell_release_no_holder(prison, cell, inmates);
  	spin_unlock_irqrestore(&prison->lock, flags);
4f81a4176   Mike Snitzer   dm thin: move bio...
211
212
  }
  EXPORT_SYMBOL_GPL(dm_cell_release_no_holder);
6beca5eb6   Joe Thornber   dm bio prison: pa...
213
  void dm_cell_error(struct dm_bio_prison *prison,
4e4cbee93   Christoph Hellwig   block: switch bio...
214
  		   struct dm_bio_prison_cell *cell, blk_status_t error)
4f81a4176   Mike Snitzer   dm thin: move bio...
215
  {
4f81a4176   Mike Snitzer   dm thin: move bio...
216
217
  	struct bio_list bios;
  	struct bio *bio;
4f81a4176   Mike Snitzer   dm thin: move bio...
218
219
  
  	bio_list_init(&bios);
adcc44472   Heinz Mauelshagen   dm bio prison: im...
220
  	dm_cell_release(prison, cell, &bios);
4f81a4176   Mike Snitzer   dm thin: move bio...
221

4246a0b63   Christoph Hellwig   block: add a bi_e...
222
  	while ((bio = bio_list_pop(&bios))) {
4e4cbee93   Christoph Hellwig   block: switch bio...
223
  		bio->bi_status = error;
4246a0b63   Christoph Hellwig   block: add a bi_e...
224
225
  		bio_endio(bio);
  	}
4f81a4176   Mike Snitzer   dm thin: move bio...
226
227
  }
  EXPORT_SYMBOL_GPL(dm_cell_error);
2d759a46b   Joe Thornber   dm thin: remap th...
228
229
230
231
232
  void dm_cell_visit_release(struct dm_bio_prison *prison,
  			   void (*visit_fn)(void *, struct dm_bio_prison_cell *),
  			   void *context,
  			   struct dm_bio_prison_cell *cell)
  {
235bc8616   Mikulas Patocka   dm bio prison: re...
233
  	spin_lock_irq(&prison->lock);
2d759a46b   Joe Thornber   dm thin: remap th...
234
235
  	visit_fn(context, cell);
  	rb_erase(&cell->node, &prison->cells);
235bc8616   Mikulas Patocka   dm bio prison: re...
236
  	spin_unlock_irq(&prison->lock);
2d759a46b   Joe Thornber   dm thin: remap th...
237
238
  }
  EXPORT_SYMBOL_GPL(dm_cell_visit_release);
3cdf93f9d   Joe Thornber   dm bio prison: ad...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  static int __promote_or_release(struct dm_bio_prison *prison,
  				struct dm_bio_prison_cell *cell)
  {
  	if (bio_list_empty(&cell->bios)) {
  		rb_erase(&cell->node, &prison->cells);
  		return 1;
  	}
  
  	cell->holder = bio_list_pop(&cell->bios);
  	return 0;
  }
  
  int dm_cell_promote_or_release(struct dm_bio_prison *prison,
  			       struct dm_bio_prison_cell *cell)
  {
  	int r;
3cdf93f9d   Joe Thornber   dm bio prison: ad...
255

235bc8616   Mikulas Patocka   dm bio prison: re...
256
  	spin_lock_irq(&prison->lock);
3cdf93f9d   Joe Thornber   dm bio prison: ad...
257
  	r = __promote_or_release(prison, cell);
235bc8616   Mikulas Patocka   dm bio prison: re...
258
  	spin_unlock_irq(&prison->lock);
3cdf93f9d   Joe Thornber   dm bio prison: ad...
259
260
261
262
  
  	return r;
  }
  EXPORT_SYMBOL_GPL(dm_cell_promote_or_release);
4f81a4176   Mike Snitzer   dm thin: move bio...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  /*----------------------------------------------------------------*/
  
  #define DEFERRED_SET_SIZE 64
  
  struct dm_deferred_entry {
  	struct dm_deferred_set *ds;
  	unsigned count;
  	struct list_head work_items;
  };
  
  struct dm_deferred_set {
  	spinlock_t lock;
  	unsigned current_entry;
  	unsigned sweeper;
  	struct dm_deferred_entry entries[DEFERRED_SET_SIZE];
  };
  
  struct dm_deferred_set *dm_deferred_set_create(void)
  {
  	int i;
  	struct dm_deferred_set *ds;
  
  	ds = kmalloc(sizeof(*ds), GFP_KERNEL);
  	if (!ds)
  		return NULL;
  
  	spin_lock_init(&ds->lock);
  	ds->current_entry = 0;
  	ds->sweeper = 0;
  	for (i = 0; i < DEFERRED_SET_SIZE; i++) {
  		ds->entries[i].ds = ds;
  		ds->entries[i].count = 0;
  		INIT_LIST_HEAD(&ds->entries[i].work_items);
  	}
  
  	return ds;
  }
  EXPORT_SYMBOL_GPL(dm_deferred_set_create);
  
  void dm_deferred_set_destroy(struct dm_deferred_set *ds)
  {
  	kfree(ds);
  }
  EXPORT_SYMBOL_GPL(dm_deferred_set_destroy);
  
  struct dm_deferred_entry *dm_deferred_entry_inc(struct dm_deferred_set *ds)
  {
  	unsigned long flags;
  	struct dm_deferred_entry *entry;
  
  	spin_lock_irqsave(&ds->lock, flags);
  	entry = ds->entries + ds->current_entry;
  	entry->count++;
  	spin_unlock_irqrestore(&ds->lock, flags);
  
  	return entry;
  }
  EXPORT_SYMBOL_GPL(dm_deferred_entry_inc);
  
  static unsigned ds_next(unsigned index)
  {
  	return (index + 1) % DEFERRED_SET_SIZE;
  }
  
  static void __sweep(struct dm_deferred_set *ds, struct list_head *head)
  {
  	while ((ds->sweeper != ds->current_entry) &&
  	       !ds->entries[ds->sweeper].count) {
  		list_splice_init(&ds->entries[ds->sweeper].work_items, head);
  		ds->sweeper = ds_next(ds->sweeper);
  	}
  
  	if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count)
  		list_splice_init(&ds->entries[ds->sweeper].work_items, head);
  }
  
  void dm_deferred_entry_dec(struct dm_deferred_entry *entry, struct list_head *head)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&entry->ds->lock, flags);
  	BUG_ON(!entry->count);
  	--entry->count;
  	__sweep(entry->ds, head);
  	spin_unlock_irqrestore(&entry->ds->lock, flags);
  }
  EXPORT_SYMBOL_GPL(dm_deferred_entry_dec);
  
  /*
   * Returns 1 if deferred or 0 if no pending items to delay job.
   */
  int dm_deferred_set_add_work(struct dm_deferred_set *ds, struct list_head *work)
  {
  	int r = 1;
4f81a4176   Mike Snitzer   dm thin: move bio...
357
  	unsigned next_entry;
235bc8616   Mikulas Patocka   dm bio prison: re...
358
  	spin_lock_irq(&ds->lock);
4f81a4176   Mike Snitzer   dm thin: move bio...
359
360
361
362
363
364
365
366
367
  	if ((ds->sweeper == ds->current_entry) &&
  	    !ds->entries[ds->current_entry].count)
  		r = 0;
  	else {
  		list_add(work, &ds->entries[ds->current_entry].work_items);
  		next_entry = ds_next(ds->current_entry);
  		if (!ds->entries[next_entry].count)
  			ds->current_entry = next_entry;
  	}
235bc8616   Mikulas Patocka   dm bio prison: re...
368
  	spin_unlock_irq(&ds->lock);
4f81a4176   Mike Snitzer   dm thin: move bio...
369
370
371
372
373
374
  
  	return r;
  }
  EXPORT_SYMBOL_GPL(dm_deferred_set_add_work);
  
  /*----------------------------------------------------------------*/
742c8fdc3   Joe Thornber   dm bio prison v2:...
375
  static int __init dm_bio_prison_init_v1(void)
4f81a4176   Mike Snitzer   dm thin: move bio...
376
377
378
379
380
381
382
  {
  	_cell_cache = KMEM_CACHE(dm_bio_prison_cell, 0);
  	if (!_cell_cache)
  		return -ENOMEM;
  
  	return 0;
  }
742c8fdc3   Joe Thornber   dm bio prison v2:...
383
  static void dm_bio_prison_exit_v1(void)
4f81a4176   Mike Snitzer   dm thin: move bio...
384
385
386
387
  {
  	kmem_cache_destroy(_cell_cache);
  	_cell_cache = NULL;
  }
742c8fdc3   Joe Thornber   dm bio prison v2:...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  static int (*_inits[])(void) __initdata = {
  	dm_bio_prison_init_v1,
  	dm_bio_prison_init_v2,
  };
  
  static void (*_exits[])(void) = {
  	dm_bio_prison_exit_v1,
  	dm_bio_prison_exit_v2,
  };
  
  static int __init dm_bio_prison_init(void)
  {
  	const int count = ARRAY_SIZE(_inits);
  
  	int r, i;
  
  	for (i = 0; i < count; i++) {
  		r = _inits[i]();
  		if (r)
  			goto bad;
  	}
  
  	return 0;
  
        bad:
  	while (i--)
  		_exits[i]();
  
  	return r;
  }
  
  static void __exit dm_bio_prison_exit(void)
  {
  	int i = ARRAY_SIZE(_exits);
  
  	while (i--)
  		_exits[i]();
  }
4f81a4176   Mike Snitzer   dm thin: move bio...
426
427
428
429
430
431
432
433
434
  /*
   * module hooks
   */
  module_init(dm_bio_prison_init);
  module_exit(dm_bio_prison_exit);
  
  MODULE_DESCRIPTION(DM_NAME " bio prison");
  MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
  MODULE_LICENSE("GPL");