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