Commit e50873c0 authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] md: fix problems with checksum handling in MD superblocks.

md currently uses csum_partial to calculate checksums for superblocks.
However this function is not consistent across all architectures.  Some
(i386) to a 32bit csum.  Some (alpha) do a 16 bit csum.  This makes it hard
for userspace to keep up.

So we provide a generic routine (that does exactly what the i386
csum_partial does) and:

- When setting the csum, use csum_partial so that old kernels will still
  recognise the superblock

- When checking the csum, allow either csum_partial or the new generic
  code to provide the right csum.  This allows user-space to just use the
  common code and always work.

Also modify the csum for version-1 superblock (which currently aren't being
used) to always user a predictable checksum algorithm.

Thanks to Mike Tran <mhtran@us.ibm.com> for noticing this.
Signed-off-by: default avatarNeil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 9c9481ab
...@@ -472,6 +472,31 @@ static unsigned int calc_sb_csum(mdp_super_t * sb) ...@@ -472,6 +472,31 @@ static unsigned int calc_sb_csum(mdp_super_t * sb)
return csum; return csum;
} }
/* csum_partial is not consistent between different architectures.
* Some (i386) do a 32bit csum. Some (alpha) do 16 bit.
* This makes it hard for user-space to know what to do.
* So we use calc_sb_csum to set the checksum to allow working
* with older kernels, but allow calc_sb_csum_common to
* be used when checking if a checksum is correct, to
* make life easier for user-space tools that might write
* a superblock.
*/
static unsigned int calc_sb_csum_common(mdp_super_t *super)
{
unsigned int disk_csum = super->sb_csum;
unsigned long long newcsum = 0;
unsigned int csum;
int i;
unsigned int *superc = (int*) super;
super->sb_csum = 0;
for (i=0; i<MD_SB_BYTES/4; i++)
newcsum+= superc[i];
csum = (newcsum& 0xffffffff) + (newcsum>>32);
super->sb_csum = disk_csum;
return csum;
}
/* /*
* Handle superblock details. * Handle superblock details.
* We want to be able to handle multiple superblock formats * We want to be able to handle multiple superblock formats
...@@ -554,7 +579,8 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version ...@@ -554,7 +579,8 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
if (sb->raid_disks <= 0) if (sb->raid_disks <= 0)
goto abort; goto abort;
if (calc_sb_csum(sb) != sb->sb_csum) { if (calc_sb_csum(sb) != sb->sb_csum &&
calc_sb_csum_common(sb) != sb->sb_csum) {
printk(KERN_WARNING "md: invalid superblock checksum on %s\n", printk(KERN_WARNING "md: invalid superblock checksum on %s\n",
b); b);
goto abort; goto abort;
...@@ -778,11 +804,21 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) ...@@ -778,11 +804,21 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb) static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb)
{ {
unsigned int disk_csum, csum; unsigned int disk_csum, csum;
unsigned long long newcsum;
int size = 256 + sb->max_dev*2; int size = 256 + sb->max_dev*2;
unsigned int *isuper = (unsigned int*)sb;
int i;
disk_csum = sb->sb_csum; disk_csum = sb->sb_csum;
sb->sb_csum = 0; sb->sb_csum = 0;
csum = csum_partial((void *)sb, size, 0); newcsum = 0;
for (i=0; size>=4; size -= 4 )
newcsum += le32_to_cpu(*isuper++);
if (size == 2)
newcsum += le16_to_cpu(*(unsigned short*) isuper);
csum = (newcsum & 0xffffffff) + (newcsum >> 32);
sb->sb_csum = disk_csum; sb->sb_csum = disk_csum;
return csum; return csum;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment