Whamcloud - gitweb
LU-241 Support crc32c with hardware accelerated instruction as one of lustre checksums
[fs/lustre-release.git] / lustre / include / obd_cksum.h
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36
37 #ifndef __OBD_CKSUM
38 #define __OBD_CKSUM
39
40 #if defined(__linux__)
41 #include <linux/obd_cksum.h>
42 #elif defined(__APPLE__)
43 #include <darwin/obd_chksum.h>
44 #elif defined(__WINNT__)
45 #include <winnt/obd_cksum.h>
46 #else
47 #error Unsupported operating system.
48 #endif
49
50 #include <lustre/lustre_idl.h>
51
52 /*
53  * Checksums
54  */
55
56 #ifndef HAVE_ARCH_CRC32
57 /* crc32_le lifted from the Linux kernel, which had the following to say:
58  *
59  * This code is in the public domain; copyright abandoned.
60  * Liability for non-performance of this code is limited to the amount
61  * you paid for it.  Since it is distributed for free, your refund will
62  * be very very small.  If it breaks, you get to keep both pieces.
63  */
64 #define CRCPOLY_LE 0xedb88320
65 /**
66  * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
67  * \param crc  seed value for computation.  ~0 for Ethernet, sometimes 0 for
68  *             other uses, or the previous crc32 value if computing incrementally.
69  * \param p  - pointer to buffer over which CRC is run
70  * \param len- length of buffer \a p
71  */
72 static inline __u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
73 {
74         int i;
75         while (len--) {
76                 crc ^= *p++;
77                 for (i = 0; i < 8; i++)
78                         crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
79         }
80         return crc;
81 }
82 #endif
83  
84 #ifdef HAVE_ADLER
85 /* Adler-32 is supported */
86 #define CHECKSUM_ADLER OBD_CKSUM_ADLER
87 #else
88 #define CHECKSUM_ADLER 0
89 #endif
90
91 #ifdef X86_FEATURE_XMM4_2
92 /* Call Nehalem+ CRC32C harware acceleration instruction on individual bytes. */
93 static inline __u32 crc32c_hw_byte(__u32 crc, unsigned char const *p,
94                                    size_t bytes)
95 {
96         while (bytes--) {
97                 __asm__ __volatile__ (
98                         ".byte 0xf2, 0xf, 0x38, 0xf0, 0xf1"
99                         : "=S"(crc)
100                         : "0"(crc), "c"(*p)
101                 );
102                 p++;
103         }
104
105         return crc;
106 }
107
108 #if BITS_PER_LONG > 32
109 #define WORD_SHIFT 3
110 #define WORD_MASK  7
111 #define REX "0x48, "
112 #else
113 #define WORD_SHIFT 2
114 #define WORD_MASK  3
115 #define REX ""
116 #endif
117
118 /* Do we need to worry about unaligned input data here? */
119 static inline __u32 crc32c_hw(__u32 crc, unsigned char const *p, size_t len)
120 {
121         unsigned int words = len >> WORD_SHIFT;
122         unsigned int bytes = len &  WORD_MASK;
123         long *ptmp = (long *)p;
124
125         while (words--) {
126                 __asm__ __volatile__(
127                         ".byte 0xf2, " REX "0xf, 0x38, 0xf1, 0xf1;"
128                         : "=S"(crc)
129                         : "0"(crc), "c"(*ptmp)
130                 );
131                 ptmp++;
132         }
133
134         if (bytes)
135                 crc = crc32c_hw_byte(crc, (unsigned char *)ptmp, bytes);
136
137         return crc;
138 }
139 #else
140 /* We should never call this unless the CPU has previously been detected to
141  * support this instruction in the SSE4.2 feature set. b=23549  */
142 static inline __u32 crc32c_hw(__u32 crc, unsigned char const *p,size_t len)
143 {
144         LBUG();
145 }
146 #endif
147
148 static inline __u32 init_checksum(cksum_type_t cksum_type)
149 {
150         switch(cksum_type) {
151         case OBD_CKSUM_CRC32C:
152                 return ~0U;
153 #ifdef HAVE_ADLER
154         case OBD_CKSUM_ADLER:
155                 return 1U;
156 #endif
157         case OBD_CKSUM_CRC32:
158                 return ~0U;
159         default:
160                 CERROR("Unknown checksum type (%x)!!!\n", cksum_type);
161                 LBUG();
162         }
163         return 0;
164 }
165
166 static inline __u32 compute_checksum(__u32 cksum, unsigned char const *p,
167                                      size_t len, cksum_type_t cksum_type)
168 {
169         switch(cksum_type) {
170         case OBD_CKSUM_CRC32C:
171                 return crc32c_hw(cksum, p, len);
172 #ifdef HAVE_ADLER
173         case OBD_CKSUM_ADLER:
174                 return adler32(cksum, p, len);
175 #endif
176         case OBD_CKSUM_CRC32:
177                 return crc32_le(cksum, p, len);
178         default:
179                 CERROR("Unknown checksum type (%x)!!!\n", cksum_type);
180                 LBUG();
181         }
182         return 0;
183 }
184
185 /* The OBD_FL_CKSUM_* flags is packed into 5 bits of o_flags, since there can
186  * only be a single checksum type per RPC.
187  *
188  * The OBD_CHECKSUM_* type bits passed in ocd_cksum_types are a 32-bit bitmask
189  * since they need to represent the full range of checksum algorithms that
190  * both the client and server can understand.
191  *
192  * In case of an unsupported types/flags we fall back to CRC32 (even though
193  * it isn't very fast) because that is supported by all clients
194  * checksums, since 1.6.5 (or earlier via patches).
195  *
196  * These flags should be listed in order of descending performance, so that
197  * in case multiple algorithms are supported the best one is used. */
198 static inline obd_flag cksum_type_pack(cksum_type_t cksum_type)
199 {
200         if (cksum_type & OBD_CKSUM_CRC32C)
201                 return OBD_FL_CKSUM_CRC32C;
202 #ifdef HAVE_ADLER
203         if (cksum_type & OBD_CKSUM_ADLER)
204                 return OBD_FL_CKSUM_ADLER;
205 #endif
206         if (unlikely(cksum_type && !(cksum_type & OBD_CKSUM_CRC32)))
207                 CWARN("unknown cksum type %x\n", cksum_type);
208
209         return OBD_FL_CKSUM_CRC32;
210 }
211
212 static inline cksum_type_t cksum_type_unpack(obd_flag o_flags)
213 {
214         switch (o_flags & OBD_FL_CKSUM_ALL) {
215         case OBD_FL_CKSUM_CRC32C:
216                 return OBD_CKSUM_CRC32C;
217         case OBD_FL_CKSUM_ADLER:
218 #ifdef HAVE_ADLER
219                 return OBD_CKSUM_ADLER;
220 #else
221                 CWARN("checksum type is set to adler32, but adler32 is not "
222                       "supported (%x)\n", o_flags);
223                 break;
224 #endif
225         default:
226                 break;
227         }
228
229         /* 1.6.4- only supported CRC32 and didn't set o_flags */
230         return OBD_CKSUM_CRC32;
231 }
232
233 /* Return a bitmask of the checksum types supported on this system.
234  *
235  * CRC32 is a required for compatibility (starting with 1.6.5),
236  * after which we could move to Adler as the base checksum type.
237  *
238  * If hardware crc32c support is not available, it is slower than Adler,
239  * so don't include it, even if it could be emulated in software. b=23549 */
240 static inline cksum_type_t cksum_types_supported(void)
241 {
242         cksum_type_t ret = OBD_CKSUM_CRC32;
243
244 #ifdef X86_FEATURE_XMM4_2
245         if (cpu_has_xmm4_2)
246                 ret |= OBD_CKSUM_CRC32C;
247 #endif
248 #ifdef HAVE_ADLER
249         ret |= OBD_CKSUM_ADLER;
250 #endif
251         return ret;
252 }
253
254 /* Select the best checksum algorithm among those supplied in the cksum_types
255  * input.
256  *
257  * Currently, calling cksum_type_pack() with a mask will return the fastest
258  * checksum type due to its ordering, but in the future we might want to
259  * determine this based on benchmarking the different algorithms quickly.
260  * Caution is advised, however, since what is fastest on a single client may
261  * not be the fastest or most efficient algorithm on the server.  */
262 static inline cksum_type_t cksum_type_select(cksum_type_t cksum_types)
263 {
264         return cksum_type_unpack(cksum_type_pack(cksum_types));
265 }
266
267 /* Checksum algorithm names. Must be defined in the same order as the
268  * OBD_CKSUM_* flags. */
269 #define DECLARE_CKSUM_NAME char *cksum_name[] = {"crc32", "adler", "crc32c"}
270
271 #endif /* __OBD_H */