cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dm-bio-prison-v1.c (10218B)


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