Whamcloud - gitweb
a48e5e9d6f5ed5e5e71a842e421d9a9afa3dc47f
[fs/lustre-release.git] / lustre / obdclass / integrity.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2018, DataDirect Networks Storage.
24  * Author: Li Xi.
25  *
26  * General data integrity functions
27  */
28 #include <linux/blkdev.h>
29 #include <linux/crc-t10dif.h>
30 #include <asm/checksum.h>
31 #include <obd_class.h>
32 #include <obd_cksum.h>
33
34 __u16 obd_dif_crc_fn(void *data, unsigned int len)
35 {
36         return cpu_to_be16(crc_t10dif(data, len));
37 }
38 EXPORT_SYMBOL(obd_dif_crc_fn);
39
40 __u16 obd_dif_ip_fn(void *data, unsigned int len)
41 {
42         return ip_compute_csum(data, len);
43 }
44 EXPORT_SYMBOL(obd_dif_ip_fn);
45
46 int obd_page_dif_generate_buffer(const char *obd_name, struct page *page,
47                                  __u32 offset, __u32 length,
48                                  __u16 *guard_start, int guard_number,
49                                  int *used_number, int sector_size,
50                                  obd_dif_csum_fn *fn)
51 {
52         unsigned int i;
53         char *data_buf;
54         __u16 *guard_buf = guard_start;
55         unsigned int data_size;
56         int used = 0;
57
58         data_buf = kmap(page) + offset;
59         for (i = 0; i < length; i += sector_size) {
60                 if (used >= guard_number) {
61                         CERROR("%s: unexpected used guard number of DIF %u/%u, "
62                                "data length %u, sector size %u: rc = %d\n",
63                                obd_name, used, guard_number, length,
64                                sector_size, -E2BIG);
65                         return -E2BIG;
66                 }
67                 data_size = length - i;
68                 if (data_size > sector_size)
69                         data_size = sector_size;
70                 *guard_buf = fn(data_buf, data_size);
71                 guard_buf++;
72                 data_buf += data_size;
73                 used++;
74         }
75         kunmap(page);
76         *used_number = used;
77
78         return 0;
79 }
80 EXPORT_SYMBOL(obd_page_dif_generate_buffer);
81
82 static int __obd_t10_performance_test(const char *obd_name,
83                                       enum cksum_types cksum_type,
84                                       struct page *data_page,
85                                       int repeat_number)
86 {
87         unsigned char cfs_alg = cksum_obd2cfs(OBD_CKSUM_T10_TOP);
88         struct cfs_crypto_hash_desc *hdesc;
89         obd_dif_csum_fn *fn = NULL;
90         unsigned int bufsize;
91         unsigned char *buffer;
92         struct page *__page;
93         __u16 *guard_start;
94         int guard_number;
95         int used_number = 0;
96         int sector_size = 0;
97         __u32 cksum;
98         int rc = 0;
99         int rc2;
100         int used;
101         int i;
102
103         obd_t10_cksum2dif(cksum_type, &fn, &sector_size);
104         if (!fn)
105                 return -EINVAL;
106
107         __page = alloc_page(GFP_KERNEL);
108         if (__page == NULL)
109                 return -ENOMEM;
110
111         hdesc = cfs_crypto_hash_init(cfs_alg, NULL, 0);
112         if (IS_ERR(hdesc)) {
113                 rc = PTR_ERR(hdesc);
114                 CERROR("%s: unable to initialize checksum hash %s: rc = %d\n",
115                        obd_name, cfs_crypto_hash_name(cfs_alg), rc);
116                 GOTO(out, rc);
117         }
118
119         buffer = kmap(__page);
120         guard_start = (__u16 *)buffer;
121         guard_number = PAGE_SIZE / sizeof(*guard_start);
122         for (i = 0; i < repeat_number; i++) {
123                 /*
124                  * The left guard number should be able to hold checksums of a
125                  * whole page
126                  */
127                 rc = obd_page_dif_generate_buffer(obd_name, data_page, 0,
128                                                   PAGE_SIZE,
129                                                   guard_start + used_number,
130                                                   guard_number - used_number,
131                                                   &used, sector_size, fn);
132                 if (rc)
133                         break;
134
135                 used_number += used;
136                 if (used_number == guard_number) {
137                         cfs_crypto_hash_update_page(hdesc, __page, 0,
138                                 used_number * sizeof(*guard_start));
139                         used_number = 0;
140                 }
141         }
142         kunmap(__page);
143         if (rc)
144                 GOTO(out_final, rc);
145
146         if (used_number != 0)
147                 cfs_crypto_hash_update_page(hdesc, __page, 0,
148                         used_number * sizeof(*guard_start));
149
150         bufsize = sizeof(cksum);
151 out_final:
152         rc2 = cfs_crypto_hash_final(hdesc, (unsigned char *)&cksum, &bufsize);
153         rc = rc ? rc : rc2;
154 out:
155         __free_page(__page);
156
157         return rc;
158 }
159
160 /**
161  *  Array of T10PI checksum algorithm speed in MByte per second
162  */
163 static int obd_t10_cksum_speeds[OBD_T10_CKSUM_MAX];
164
165 static enum obd_t10_cksum_type
166 obd_t10_cksum2type(enum cksum_types cksum_type)
167 {
168         switch (cksum_type) {
169         case OBD_CKSUM_T10IP512:
170                 return OBD_T10_CKSUM_IP512;
171         case OBD_CKSUM_T10IP4K:
172                 return OBD_T10_CKSUM_IP4K;
173         case OBD_CKSUM_T10CRC512:
174                 return OBD_T10_CKSUM_CRC512;
175         case OBD_CKSUM_T10CRC4K:
176                 return OBD_T10_CKSUM_CRC4K;
177         default:
178                 return OBD_T10_CKSUM_UNKNOWN;
179         }
180 }
181
182 static const char *obd_t10_cksum_name(enum obd_t10_cksum_type index)
183 {
184         DECLARE_CKSUM_NAME;
185
186         /* Need to skip "crc32", "adler", "crc32c", "reserved" */
187         return cksum_name[3 + index];
188 }
189
190 /**
191  * Compute the speed of specified T10PI checksum type
192  *
193  * Run a speed test on the given T10PI checksum on buffer using a 1MB buffer
194  * size. This is a reasonable buffer size for Lustre RPCs, even if the actual
195  * RPC size is larger or smaller.
196  *
197  * The speed is stored internally in the obd_t10_cksum_speeds[] array, and
198  * is available through the obd_t10_cksum_speed() function.
199  *
200  * This function needs to stay the same as cfs_crypto_performance_test() so
201  * that the speeds are comparable. And this function should reflect the real
202  * cost of the checksum calculation.
203  *
204  * \param[in] obd_name          name of the OBD device
205  * \param[in] cksum_type        checksum type (OBD_CKSUM_T10*)
206  */
207 static void obd_t10_performance_test(const char *obd_name,
208                                      enum cksum_types cksum_type)
209 {
210         enum obd_t10_cksum_type index = obd_t10_cksum2type(cksum_type);
211         const int buf_len = max(PAGE_SIZE, 1048576UL);
212         unsigned long bcount;
213         unsigned long start;
214         unsigned long end;
215         struct page *page;
216         int rc = 0;
217         void *buf;
218
219         page = alloc_page(GFP_KERNEL);
220         if (page == NULL) {
221                 rc = -ENOMEM;
222                 goto out;
223         }
224
225         buf = kmap(page);
226         memset(buf, 0xAD, PAGE_SIZE);
227         kunmap(page);
228
229         for (start = jiffies, end = start + msecs_to_jiffies(MSEC_PER_SEC / 4),
230              bcount = 0; time_before(jiffies, end) && rc == 0; bcount++) {
231                 rc = __obd_t10_performance_test(obd_name, cksum_type, page,
232                                                 buf_len / PAGE_SIZE);
233                 if (rc)
234                         break;
235         }
236         end = jiffies;
237         __free_page(page);
238 out:
239         if (rc) {
240                 obd_t10_cksum_speeds[index] = rc;
241                 CDEBUG(D_INFO, "%s: T10 checksum algorithm %s test error: "
242                        "rc = %d\n", obd_name, obd_t10_cksum_name(index), rc);
243         } else {
244                 unsigned long tmp;
245
246                 tmp = ((bcount * buf_len / jiffies_to_msecs(end - start)) *
247                        1000) / (1024 * 1024);
248                 obd_t10_cksum_speeds[index] = (int)tmp;
249                 CDEBUG(D_CONFIG, "%s: T10 checksum algorithm %s speed = %d "
250                        "MB/s\n", obd_name, obd_t10_cksum_name(index),
251                        obd_t10_cksum_speeds[index]);
252         }
253 }
254
255 int obd_t10_cksum_speed(const char *obd_name,
256                         enum cksum_types cksum_type)
257 {
258         enum obd_t10_cksum_type index = obd_t10_cksum2type(cksum_type);
259
260         if (unlikely(obd_t10_cksum_speeds[index] == 0)) {
261                 static DEFINE_MUTEX(obd_t10_cksum_speed_mutex);
262
263                 mutex_lock(&obd_t10_cksum_speed_mutex);
264                 if (obd_t10_cksum_speeds[index] == 0)
265                         obd_t10_performance_test(obd_name, cksum_type);
266                 mutex_unlock(&obd_t10_cksum_speed_mutex);
267         }
268
269         return obd_t10_cksum_speeds[index];
270 }
271 EXPORT_SYMBOL(obd_t10_cksum_speed);