mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 01:51:34 +00:00
cae028df53
Nick Piggin reported that the CRC overhead in an fsync heavy workload was higher than expected on a Power8 machine. Part of this was to do with the fact that the power8 CRC implementation is not efficient for CRC lengths of less than 512 bytes, and so the way we split the CRCs over the CRC field means a lot of the CRCs are reduced to being less than than optimal size. To optimise this, change the CRC update mechanism to zero the CRC field first, and then compute the CRC in one pass over the buffer and write the result back into the buffer. We can do this safely because anything writing a CRC has exclusive access to the buffer the CRC is being calculated over. We leave the CRC verify code the same - it still splits the CRC calculation - because we do not want read-only operations modifying the underlying buffer. This is because read-only operations may not have an exclusive access to the buffer guaranteed, and so temporary modifications could leak out to to other processes accessing the buffer concurrently. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Dave Chinner <david@fromorbit.com>
82 lines
2.3 KiB
C
82 lines
2.3 KiB
C
#ifndef _XFS_CKSUM_H
|
|
#define _XFS_CKSUM_H 1
|
|
|
|
#define XFS_CRC_SEED (~(__uint32_t)0)
|
|
|
|
/*
|
|
* Calculate the intermediate checksum for a buffer that has the CRC field
|
|
* inside it. The offset of the 32bit crc fields is passed as the
|
|
* cksum_offset parameter. We do not modify the buffer during verification,
|
|
* hence we have to split the CRC calculation across the cksum_offset.
|
|
*/
|
|
static inline __uint32_t
|
|
xfs_start_cksum_safe(char *buffer, size_t length, unsigned long cksum_offset)
|
|
{
|
|
__uint32_t zero = 0;
|
|
__uint32_t crc;
|
|
|
|
/* Calculate CRC up to the checksum. */
|
|
crc = crc32c(XFS_CRC_SEED, buffer, cksum_offset);
|
|
|
|
/* Skip checksum field */
|
|
crc = crc32c(crc, &zero, sizeof(__u32));
|
|
|
|
/* Calculate the rest of the CRC. */
|
|
return crc32c(crc, &buffer[cksum_offset + sizeof(__be32)],
|
|
length - (cksum_offset + sizeof(__be32)));
|
|
}
|
|
|
|
/*
|
|
* Fast CRC method where the buffer is modified. Callers must have exclusive
|
|
* access to the buffer while the calculation takes place.
|
|
*/
|
|
static inline __uint32_t
|
|
xfs_start_cksum_update(char *buffer, size_t length, unsigned long cksum_offset)
|
|
{
|
|
/* zero the CRC field */
|
|
*(__le32 *)(buffer + cksum_offset) = 0;
|
|
|
|
/* single pass CRC calculation for the entire buffer */
|
|
return crc32c(XFS_CRC_SEED, buffer, length);
|
|
}
|
|
|
|
/*
|
|
* Convert the intermediate checksum to the final ondisk format.
|
|
*
|
|
* The CRC32c calculation uses LE format even on BE machines, but returns the
|
|
* result in host endian format. Hence we need to byte swap it back to LE format
|
|
* so that it is consistent on disk.
|
|
*/
|
|
static inline __le32
|
|
xfs_end_cksum(__uint32_t crc)
|
|
{
|
|
return ~cpu_to_le32(crc);
|
|
}
|
|
|
|
/*
|
|
* Helper to generate the checksum for a buffer.
|
|
*
|
|
* This modifies the buffer temporarily - callers must have exclusive
|
|
* access to the buffer while the calculation takes place.
|
|
*/
|
|
static inline void
|
|
xfs_update_cksum(char *buffer, size_t length, unsigned long cksum_offset)
|
|
{
|
|
__uint32_t crc = xfs_start_cksum_update(buffer, length, cksum_offset);
|
|
|
|
*(__le32 *)(buffer + cksum_offset) = xfs_end_cksum(crc);
|
|
}
|
|
|
|
/*
|
|
* Helper to verify the checksum for a buffer.
|
|
*/
|
|
static inline int
|
|
xfs_verify_cksum(char *buffer, size_t length, unsigned long cksum_offset)
|
|
{
|
|
__uint32_t crc = xfs_start_cksum_safe(buffer, length, cksum_offset);
|
|
|
|
return *(__le32 *)(buffer + cksum_offset) == xfs_end_cksum(crc);
|
|
}
|
|
|
|
#endif /* _XFS_CKSUM_H */
|