Whamcloud - gitweb
New tag 2.15.63
[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 #if IS_ENABLED(CONFIG_CRC_T10DIF)
35 __be16 obd_dif_crc_fn(void *data, unsigned int len)
36 {
37         return cpu_to_be16(crc_t10dif(data, len));
38 }
39 EXPORT_SYMBOL(obd_dif_crc_fn);
40
41 __be16 obd_dif_ip_fn(void *data, unsigned int len)
42 {
43         return (__force __be16)ip_compute_csum(data, len);
44 }
45 EXPORT_SYMBOL(obd_dif_ip_fn);
46
47 int obd_page_dif_generate_buffer(const char *obd_name, struct page *page,
48                                  __u32 start, __u32 length,
49                                  __be16 *guard_start, int guard_number,
50                                  int *used_number, int sector_size,
51                                  obd_dif_csum_fn *fn)
52 {
53         unsigned int off = start;
54         unsigned int end = start + length;
55         char *data_buf;
56         __be16 *guard_buf = guard_start;
57         unsigned int data_size;
58         int guard_used = 0;
59         int rc = 0;
60
61         data_buf = kmap(page) + start;
62         while (off < end) {
63                 if (guard_used >= guard_number) {
64                         rc = -E2BIG;
65                         CERROR("%s: used %u >= guard %u, data %u+%u, sector_size %u: rc = %d\n",
66                                obd_name, guard_used, guard_number, start,
67                                length, sector_size, rc);
68                         goto out;
69                 }
70                 data_size = min(round_up(off + 1, sector_size), end) - off;
71                 *guard_buf = fn(data_buf, data_size);
72                 guard_buf++;
73                 guard_used++;
74                 data_buf += data_size;
75                 off += data_size;
76         }
77         *used_number = guard_used;
78 out:
79         kunmap(page);
80
81         return rc;
82 }
83 EXPORT_SYMBOL(obd_page_dif_generate_buffer);
84
85 static int __obd_t10_performance_test(const char *obd_name,
86                                       enum cksum_types cksum_type,
87                                       struct page *data_page,
88                                       int repeat_number)
89 {
90         unsigned char cfs_alg = cksum_obd2cfs(OBD_CKSUM_T10_TOP);
91         struct ahash_request *req;
92         obd_dif_csum_fn *fn = NULL;
93         unsigned int bufsize;
94         unsigned char *buffer;
95         struct page *__page;
96         __be16 *guard_start;
97         int guard_number;
98         int used_number = 0;
99         int sector_size = 0;
100         __u32 cksum;
101         int rc = 0;
102         int rc2;
103         int used;
104         int i;
105
106         obd_t10_cksum2dif(cksum_type, &fn, &sector_size);
107         if (!fn)
108                 return -EINVAL;
109
110         __page = alloc_page(GFP_KERNEL);
111         if (__page == NULL)
112                 return -ENOMEM;
113
114         req = cfs_crypto_hash_init(cfs_alg, NULL, 0);
115         if (IS_ERR(req)) {
116                 rc = PTR_ERR(req);
117                 CERROR("%s: unable to initialize checksum hash %s: rc = %d\n",
118                        obd_name, cfs_crypto_hash_name(cfs_alg), rc);
119                 GOTO(out, rc);
120         }
121
122         buffer = kmap(__page);
123         guard_start = (__be16 *)buffer;
124         guard_number = PAGE_SIZE / sizeof(*guard_start);
125         for (i = 0; i < repeat_number; i++) {
126                 /*
127                  * The left guard number should be able to hold checksums of a
128                  * whole page
129                  */
130                 rc = obd_page_dif_generate_buffer(obd_name, data_page, 0,
131                                                   PAGE_SIZE,
132                                                   guard_start + used_number,
133                                                   guard_number - used_number,
134                                                   &used, sector_size, fn);
135                 if (rc)
136                         break;
137
138                 used_number += used;
139                 if (used_number == guard_number) {
140                         cfs_crypto_hash_update_page(req, __page, 0,
141                                 used_number * sizeof(*guard_start));
142                         used_number = 0;
143                 }
144         }
145         kunmap(__page);
146         if (rc)
147                 GOTO(out_final, rc);
148
149         if (used_number != 0)
150                 cfs_crypto_hash_update_page(req, __page, 0,
151                         used_number * sizeof(*guard_start));
152
153         bufsize = sizeof(cksum);
154 out_final:
155         rc2 = cfs_crypto_hash_final(req, (unsigned char *)&cksum, &bufsize);
156         rc = rc ? rc : rc2;
157 out:
158         __free_page(__page);
159
160         return rc;
161 }
162
163 /**
164  *  Array of T10PI checksum algorithm speed in MByte per second
165  */
166 static int obd_t10_cksum_speeds[OBD_T10_CKSUM_MAX];
167
168 static enum obd_t10_cksum_type
169 obd_t10_cksum2type(enum cksum_types cksum_type)
170 {
171         switch (cksum_type) {
172         case OBD_CKSUM_T10IP512:
173                 return OBD_T10_CKSUM_IP512;
174         case OBD_CKSUM_T10IP4K:
175                 return OBD_T10_CKSUM_IP4K;
176         case OBD_CKSUM_T10CRC512:
177                 return OBD_T10_CKSUM_CRC512;
178         case OBD_CKSUM_T10CRC4K:
179                 return OBD_T10_CKSUM_CRC4K;
180         default:
181                 return OBD_T10_CKSUM_UNKNOWN;
182         }
183 }
184
185 static const char *obd_t10_cksum_name(enum obd_t10_cksum_type index)
186 {
187         DECLARE_CKSUM_NAME;
188
189         /* Need to skip "crc32", "adler", "crc32c", "reserved" */
190         return cksum_name[3 + index];
191 }
192
193 /**
194  * Compute the speed of specified T10PI checksum type
195  *
196  * Run a speed test on the given T10PI checksum on buffer using a 1MB buffer
197  * size. This is a reasonable buffer size for Lustre RPCs, even if the actual
198  * RPC size is larger or smaller.
199  *
200  * The speed is stored internally in the obd_t10_cksum_speeds[] array, and
201  * is available through the obd_t10_cksum_speed() function.
202  *
203  * This function needs to stay the same as cfs_crypto_performance_test() so
204  * that the speeds are comparable. And this function should reflect the real
205  * cost of the checksum calculation.
206  *
207  * \param[in] obd_name          name of the OBD device
208  * \param[in] cksum_type        checksum type (OBD_CKSUM_T10*)
209  */
210 static void obd_t10_performance_test(const char *obd_name,
211                                      enum cksum_types cksum_type)
212 {
213         enum obd_t10_cksum_type index = obd_t10_cksum2type(cksum_type);
214         const int buf_len = max(PAGE_SIZE, 1048576UL);
215         unsigned long bcount;
216         unsigned long start;
217         unsigned long end;
218         struct page *page;
219         int rc = 0;
220         void *buf;
221
222         page = alloc_page(GFP_KERNEL);
223         if (page == NULL) {
224                 rc = -ENOMEM;
225                 goto out;
226         }
227
228         buf = kmap(page);
229         memset(buf, 0xAD, PAGE_SIZE);
230         kunmap(page);
231
232         for (start = jiffies, end = start + cfs_time_seconds(1) / 4,
233              bcount = 0; time_before(jiffies, end) && rc == 0; bcount++) {
234                 rc = __obd_t10_performance_test(obd_name, cksum_type, page,
235                                                 buf_len >> PAGE_SHIFT);
236                 if (rc)
237                         break;
238         }
239         end = jiffies;
240         __free_page(page);
241 out:
242         if (rc) {
243                 obd_t10_cksum_speeds[index] = rc;
244                 CDEBUG(D_INFO, "%s: T10 checksum algorithm %s test error: "
245                        "rc = %d\n", obd_name, obd_t10_cksum_name(index), rc);
246         } else {
247                 unsigned long tmp;
248
249                 tmp = ((bcount * buf_len / jiffies_to_msecs(end - start)) *
250                        1000) / (1024 * 1024);
251                 obd_t10_cksum_speeds[index] = (int)tmp;
252                 CDEBUG(D_CONFIG, "%s: T10 checksum algorithm %s speed = %d "
253                        "MB/s\n", obd_name, obd_t10_cksum_name(index),
254                        obd_t10_cksum_speeds[index]);
255         }
256 }
257 #endif /* CONFIG_CRC_T10DIF */
258
259 int obd_t10_cksum_speed(const char *obd_name,
260                         enum cksum_types cksum_type)
261 {
262 #if IS_ENABLED(CONFIG_CRC_T10DIF)
263         enum obd_t10_cksum_type index = obd_t10_cksum2type(cksum_type);
264
265         if (unlikely(obd_t10_cksum_speeds[index] == 0)) {
266                 static DEFINE_MUTEX(obd_t10_cksum_speed_mutex);
267
268                 mutex_lock(&obd_t10_cksum_speed_mutex);
269                 if (obd_t10_cksum_speeds[index] == 0)
270                         obd_t10_performance_test(obd_name, cksum_type);
271                 mutex_unlock(&obd_t10_cksum_speed_mutex);
272         }
273
274         return obd_t10_cksum_speeds[index];
275 #else /* !CONFIG_CRC_T10DIF */
276         return 0;
277 #endif /* !CONFIG_CRC_T10DIF */
278 }
279 EXPORT_SYMBOL(obd_t10_cksum_speed);