-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathdm-dedup-hash.c
145 lines (120 loc) · 3.61 KB
/
dm-dedup-hash.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
* Copyright (C) 2012-2014 Vasily Tarasov
* Copyright (C) 2012-2014 Geoff Kuenning
* Copyright (C) 2012-2014 Sonam Mandal
* Copyright (C) 2012-2014 Karthikeyani Palanisami
* Copyright (C) 2012-2014 Philip Shilane
* Copyright (C) 2012-2014 Sagar Trehan
* Copyright (C) 2012-2014 Erez Zadok
*
* This file is released under the GPL.
*/
#include "dm-dedup-target.h"
#include "dm-dedup-hash.h"
#include <linux/atomic.h>
#include <linux/blk_types.h>
/*
* We are declaring and initalizaing global hash_desc, because
* we need to do hash computation in endio function, and this
* function is called in softirq context. Hence we are not
* allowed to perform any operation on that path which can sleep.
* And tfm allocation in hash_desc, at one point, tries to take
* semaphore and hence tries to sleep. And because of this we get
* BUG, which complains "Scheduling while atomic". Hence to avoid
* this scenario, we moved the declaration and initialization out
* of critical path.
*/
static struct hash_desc *slot_to_desc(struct hash_desc_table *desc_table,
unsigned long slot)
{
BUG_ON(slot >= DEDUP_HASH_DESC_COUNT);
return &(desc_table->desc[slot]);
}
struct hash_desc_table *desc_table_init(char *hash_alg)
{
int i = 0;
struct hash_desc *desc;
struct hash_desc_table *desc_table;
desc_table = kmalloc(sizeof(struct hash_desc_table), GFP_NOIO);
if (!desc_table)
return ERR_PTR(-ENOMEM);
for (i = 0; i < DEDUP_HASH_DESC_COUNT; i++) {
desc_table->free_bitmap[i] = true;
desc = &desc_table->desc[i];
desc->flags = 0;
desc->tfm = crypto_alloc_hash(hash_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(desc->tfm))
return (struct hash_desc_table *)desc->tfm;
}
atomic_long_set(&(desc_table->slot_counter), 0);
return desc_table;
}
void desc_table_deinit(struct hash_desc_table *desc_table)
{
int i = 0;
struct hash_desc *desc;
for (i = 0; i < DEDUP_HASH_DESC_COUNT; i++) {
desc = desc_table->desc + i;
crypto_free_hash(desc->tfm);
}
kfree(desc_table);
desc_table = NULL;
}
static int get_next_slot(struct hash_desc_table *desc_table)
{
unsigned long num = 0;
int count = 0;
do {
if (count == DEDUP_HASH_DESC_COUNT)
return -EBUSY;
count++;
num = atomic_long_inc_return(&(desc_table->slot_counter));
num = num % DEDUP_HASH_DESC_COUNT;
} while (!desc_table->free_bitmap[num]);
/* XXX: Possibility of race condition here. As checking of bitmap
* and its setting is not happening in same step. But it will
* work for now, as we declare atleast twice more hash_desc
* then number of threads.
*/
desc_table->free_bitmap[num] = false;
return num;
}
static void put_slot(struct hash_desc_table *desc_table, unsigned long slot)
{
BUG_ON(slot >= DEDUP_HASH_DESC_COUNT);
BUG_ON(desc_table->free_bitmap[slot]);
desc_table->free_bitmap[slot] = true;
}
unsigned int get_hash_digestsize(struct hash_desc_table *desc_table)
{
unsigned long slot;
struct hash_desc *desc;
slot = get_next_slot(desc_table);
desc = slot_to_desc(desc_table, slot);
return crypto_hash_digestsize(desc->tfm);
}
int compute_hash_bio(struct hash_desc_table *desc_table,
struct bio *bio, char *hash)
{
struct scatterlist sg;
int ret = 0;
unsigned long slot;
struct bio_vec bvec;
struct bvec_iter iter;
struct hash_desc *desc;
slot = get_next_slot(desc_table);
desc = slot_to_desc(desc_table, slot);
ret = crypto_hash_init(desc);
if (ret)
goto out;
sg_init_table(&sg, 1);
__bio_for_each_segment(bvec, bio, iter, bio->bi_iter) {
sg_set_page(&sg, bvec.bv_page, bvec.bv_len,
bvec.bv_offset);
crypto_hash_update(desc, &sg, sg.length);
}
crypto_hash_final(desc, hash);
out:
put_slot(desc_table, slot);
return ret;
}