1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

RAID: Add support for RAID10

This patch adds support for RAID10.  It is not the default at this
stage.  The user needs to specify '--type raid10' if they would like
RAID10 instead of stacked mirror over stripe.
This commit is contained in:
Jonathan Brassow 2012-08-24 15:34:19 -05:00
parent 99d1e264a8
commit 4047e4dfb1
11 changed files with 218 additions and 7 deletions

View File

@ -1,5 +1,6 @@
Version 2.02.98 Version 2.02.98
================================= =================================
Add RAID10 support.
Reuse _reload_lv() in more lvconvert functions. Reuse _reload_lv() in more lvconvert functions.
Fix dereference of NULL in lvmetad error path logging. Fix dereference of NULL in lvmetad error path logging.
Fix buffer memory leak in lvmetad logging. Fix buffer memory leak in lvmetad logging.

View File

@ -476,10 +476,10 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
if (lv_is_thin_type(lv)) if (lv_is_thin_type(lv))
repstr[6] = 't'; repstr[6] = 't';
else if (lv_is_mirror_type(lv))
repstr[6] = 'm';
else if (lv_is_raid_type(lv)) else if (lv_is_raid_type(lv))
repstr[6] = 'r'; repstr[6] = 'r';
else if (lv_is_mirror_type(lv))
repstr[6] = 'm';
else if (lv_is_cow(lv) || lv_is_origin(lv)) else if (lv_is_cow(lv) || lv_is_origin(lv))
repstr[6] = 's'; repstr[6] = 's';
else if (lv_has_unknown_segments(lv)) else if (lv_has_unknown_segments(lv))

View File

@ -710,6 +710,14 @@ static uint32_t _calc_area_multiple(const struct segment_type *segtype,
return area_count - segtype->parity_devs; return area_count - segtype->parity_devs;
} }
/* RAID10 - only has 2-way mirror right now */
if (!strcmp(segtype->name, "raid10")) {
// FIXME: I'd like the 'stripes' arg always given
if (!stripes)
return area_count / 2;
return stripes;
}
/* Mirrored stripes */ /* Mirrored stripes */
if (stripes) if (stripes)
return stripes; return stripes;

View File

@ -114,6 +114,10 @@ uint32_t lv_mirror_count(const struct logical_volume *lv)
seg = first_seg(lv); seg = first_seg(lv);
/* FIXME: RAID10 only supports 2 copies right now */
if (!strcmp(seg->segtype->name, "raid10"))
return 2;
if (lv->status & PVMOVE) if (lv->status & PVMOVE)
return seg->area_count; return seg->area_count;

View File

@ -1620,6 +1620,24 @@ int lv_raid_replace(struct logical_volume *lv,
raid_seg->segtype->parity_devs, raid_seg->segtype->parity_devs,
raid_seg->segtype->name, lv->vg->name, lv->name); raid_seg->segtype->name, lv->vg->name, lv->name);
return 0; return 0;
} else if (!strcmp(raid_seg->segtype->name, "raid10")) {
uint32_t i, rebuilds_per_group = 0;
/* FIXME: We only support 2-way mirrors in RAID10 currently */
uint32_t copies = 2;
for (i = 0; i < raid_seg->area_count * copies; i++) {
s = i % raid_seg->area_count;
if (!(i % copies))
rebuilds_per_group = 0;
if (_lv_is_on_pvs(seg_lv(raid_seg, s), remove_pvs) ||
_lv_is_on_pvs(seg_metalv(raid_seg, s), remove_pvs))
rebuilds_per_group++;
if (rebuilds_per_group >= copies) {
log_error("Unable to replace all the devices "
"in a RAID10 mirror group.");
return 0;
}
}
} }
/* /*

View File

@ -379,6 +379,20 @@ static struct segment_type *_init_raid1_segtype(struct cmd_context *cmd)
return segtype; return segtype;
} }
static struct segment_type *_init_raid10_segtype(struct cmd_context *cmd)
{
struct segment_type *segtype;
segtype = _init_raid_segtype(cmd, "raid10");
if (!segtype)
return NULL;
segtype->flags |= SEG_AREAS_MIRRORED;
segtype->parity_devs = 0;
return segtype;
}
static struct segment_type *_init_raid4_segtype(struct cmd_context *cmd) static struct segment_type *_init_raid4_segtype(struct cmd_context *cmd)
{ {
return _init_raid_segtype(cmd, "raid4"); return _init_raid_segtype(cmd, "raid4");
@ -441,6 +455,7 @@ int init_multiple_segtypes(struct cmd_context *cmd, struct segtype_library *segl
unsigned i = 0; unsigned i = 0;
struct segment_type *(*raid_segtype_fn[])(struct cmd_context *) = { struct segment_type *(*raid_segtype_fn[])(struct cmd_context *) = {
_init_raid1_segtype, _init_raid1_segtype,
_init_raid10_segtype,
_init_raid4_segtype, _init_raid4_segtype,
_init_raid5_segtype, _init_raid5_segtype,
_init_raid5_la_segtype, _init_raid5_la_segtype,

View File

@ -41,6 +41,7 @@ enum {
SEG_THIN_POOL, SEG_THIN_POOL,
SEG_THIN, SEG_THIN,
SEG_RAID1, SEG_RAID1,
SEG_RAID10,
SEG_RAID4, SEG_RAID4,
SEG_RAID5_LA, SEG_RAID5_LA,
SEG_RAID5_RA, SEG_RAID5_RA,
@ -72,6 +73,7 @@ struct {
{ SEG_THIN_POOL, "thin-pool"}, { SEG_THIN_POOL, "thin-pool"},
{ SEG_THIN, "thin"}, { SEG_THIN, "thin"},
{ SEG_RAID1, "raid1"}, { SEG_RAID1, "raid1"},
{ SEG_RAID10, "raid10"},
{ SEG_RAID4, "raid4"}, { SEG_RAID4, "raid4"},
{ SEG_RAID5_LA, "raid5_la"}, { SEG_RAID5_LA, "raid5_la"},
{ SEG_RAID5_RA, "raid5_ra"}, { SEG_RAID5_RA, "raid5_ra"},
@ -1912,6 +1914,7 @@ static int _emit_areas_line(struct dm_task *dmt __attribute__((unused)),
} }
break; break;
case SEG_RAID1: case SEG_RAID1:
case SEG_RAID10:
case SEG_RAID4: case SEG_RAID4:
case SEG_RAID5_LA: case SEG_RAID5_LA:
case SEG_RAID5_RA: case SEG_RAID5_RA:
@ -2265,6 +2268,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
seg->iv_offset : *seg_start); seg->iv_offset : *seg_start);
break; break;
case SEG_RAID1: case SEG_RAID1:
case SEG_RAID10:
case SEG_RAID4: case SEG_RAID4:
case SEG_RAID5_LA: case SEG_RAID5_LA:
case SEG_RAID5_RA: case SEG_RAID5_RA:

View File

@ -11,12 +11,23 @@
. lib/test . lib/test
get_image_pvs() {
local d
local images=""
for d in `ls /dev/mapper/${1}-${2}_?image_*`; do
images="$images `basename $d | sed s:-:/:`"
done
lvs --noheadings -a -o devices $images | sed s/\(.\)//
}
######################################################## ########################################################
# MAIN # MAIN
######################################################## ########################################################
aux target_at_least dm-raid 1 1 0 || skip aux target_at_least dm-raid 1 1 0 || skip
aux prepare_pvs 5 80 # 9 PVs needed for RAID10 testing (3-stripes/2-mirror - replacing 3 devs)
aux prepare_pvs 9 80
vgcreate -c n -s 256k $vg $(cat DEVICES) vgcreate -c n -s 256k $vg $(cat DEVICES)
########################################### ###########################################
@ -132,3 +143,99 @@ for i in 1 2 3 ; do
lvconvert --type raid1 $vg/$lv1 lvconvert --type raid1 $vg/$lv1
lvremove -ff $vg lvremove -ff $vg
done done
###########################################
# Device Replacement Testing
###########################################
# RAID1: Replace up to n-1 devices - trying different combinations
# Test for 2-way to 4-way RAID1 LVs
for i in {1..3}; do
lvcreate --type raid1 -m $i -l 2 -n $lv1 $vg
for j in $(seq $(($i + 1))); do # The number of devs to replace at once
for o in $(seq 0 $i); do # The offset into the device list
replace=""
devices=( $(get_image_pvs $vg $lv1) )
for k in $(seq $j); do
index=$((($k + $o) % ($i + 1)))
replace="$replace --replace ${devices[$index]}"
done
aux wait_for_sync $vg $lv1
if [ $j -ge $((i + 1)) ]; then
# Can't replace all at once.
not lvconvert $replace $vg/$lv1
else
lvconvert $replace $vg/$lv1
fi
done
done
lvremove -ff $vg
done
# RAID 4/5/6 (can replace up to 'parity' devices)
for i in 4 5 6; do
lvcreate --type raid$i -i 3 -l 3 -n $lv1 $vg
if [ $i -eq 6 ]; then
dev_cnt=5
limit=2
else
dev_cnt=4
limit=1
fi
for j in {1..3}; do
for o in $(seq 0 $i); do
replace=""
devices=( $(get_image_pvs $vg $lv1) )
for k in $(seq $j); do
index=$((($k + $o) % $dev_cnt))
replace="$replace --replace ${devices[$index]}"
done
aux wait_for_sync $vg $lv1
if [ $j -gt $limit ]; then
not lvconvert $replace $vg/$lv1
else
lvconvert $replace $vg/$lv1
fi
done
done
lvremove -ff $vg
done
# RAID10: Can replace 'copies - 1' devices from each stripe
# Tests are run on 2-way mirror, 3-way stripe RAID10
aux target_at_least dm-raid 1 3 1 || skip
lvcreate --type raid10 -m 1 -i 3 -l 3 -n $lv1 $vg
aux wait_for_sync $vg $lv1
# Can replace any single device
for i in $(get_image_pvs $vg $lv1); do
lvconvert --replace $i $vg/$lv1
aux wait_for_sync $vg $lv1
done
# Can't replace adjacent devices
devices=( $(get_image_pvs $vg $lv1) )
not lvconvert --replace ${devices[0]} --replace ${devices[1]} $vg/$lv1
not lvconvert --replace ${devices[2]} --replace ${devices[3]} $vg/$lv1
not lvconvert --replace ${devices[4]} --replace ${devices[5]} $vg/$lv1
# Can replace non-adjacent devices
for i in 0 1; do
lvconvert \
--replace ${devices[$i]} \
--replace ${devices[$(($i + 2))]} \
--replace ${devices[$(($i + 4))]} \
$vg/$lv1
aux wait_for_sync $vg $lv1
done

View File

@ -16,7 +16,7 @@
######################################################## ########################################################
aux target_at_least dm-raid 1 1 0 || skip aux target_at_least dm-raid 1 1 0 || skip
aux prepare_pvs 5 20 aux prepare_pvs 6 20 # 6 devices for RAID10 (2-mirror,3-stripe) test
vgcreate -c n -s 512k $vg $(cat DEVICES) vgcreate -c n -s 512k $vg $(cat DEVICES)
########################################### ###########################################
@ -48,6 +48,25 @@ for i in raid4 \
lvremove -ff $vg lvremove -ff $vg
done done
#
# Create RAID10:
#
aux target_at_least dm-raid 1 3 0 || skip
# Should not allow more than 2-way mirror
not lvcreate --type raid10 -m 2 -i 2 -l 2 -n $lv1 $vg
# 2-way mirror, 2-stripes
lvcreate --type raid10 -m 1 -i 2 -l 2 -n $lv1 $vg
aux wait_for_sync $vg $lv1
lvremove -ff $vg
# 2-way mirror, 3-stripes
lvcreate --type raid10 -m 1 -i 3 -l 3 -n $lv1 $vg
aux wait_for_sync $vg $lv1
lvremove -ff $vg
# #
# FIXME: Add tests that specify particular PVs to use for creation # FIXME: Add tests that specify particular PVs to use for creation
# #

View File

@ -702,6 +702,10 @@ static int _lvcreate_params(struct lvcreate_params *lp,
/* Set default segtype */ /* Set default segtype */
if (arg_count(cmd, mirrors_ARG)) if (arg_count(cmd, mirrors_ARG))
/*
* FIXME: Add default setting for when -i and -m arguments
* are both given. We should default to "raid10".
*/
segtype_str = find_config_tree_str(cmd, "global/mirror_segtype_default", DEFAULT_MIRROR_SEGTYPE); segtype_str = find_config_tree_str(cmd, "global/mirror_segtype_default", DEFAULT_MIRROR_SEGTYPE);
else if (arg_count(cmd, thin_ARG) || arg_count(cmd, thinpool_ARG)) else if (arg_count(cmd, thin_ARG) || arg_count(cmd, thinpool_ARG))
segtype_str = "thin"; segtype_str = "thin";
@ -735,7 +739,7 @@ static int _lvcreate_params(struct lvcreate_params *lp,
lp->mirrors = 1; lp->mirrors = 1;
/* Default to 2 mirrored areas if '--type mirror|raid1' */ /* Default to 2 mirrored areas if '--type mirror|raid1|raid10' */
if (segtype_is_mirrored(lp->segtype)) if (segtype_is_mirrored(lp->segtype))
lp->mirrors = 2; lp->mirrors = 2;
@ -748,6 +752,18 @@ static int _lvcreate_params(struct lvcreate_params *lp,
} }
log_print("Redundant mirrors argument: default is 0"); log_print("Redundant mirrors argument: default is 0");
} }
if ((lp->mirrors > 2) && !strcmp(lp->segtype->name, "raid10")) {
/*
* FIXME: When RAID10 is no longer limited to
* 2-way mirror, 'lv_mirror_count()'
* must also change for RAID10.
*/
log_error("RAID10 currently supports "
"only 2-way mirroring (i.e. '-m 1')");
return 0;
}
if (arg_sign_value(cmd, mirrors_ARG, SIGN_NONE) == SIGN_MINUS) { if (arg_sign_value(cmd, mirrors_ARG, SIGN_NONE) == SIGN_MINUS) {
log_error("Mirrors argument may not be negative"); log_error("Mirrors argument may not be negative");
return 0; return 0;
@ -787,6 +803,16 @@ static int _lvcreate_params(struct lvcreate_params *lp,
log_error("%s: Required device-mapper target(s) not " log_error("%s: Required device-mapper target(s) not "
"detected in your kernel", lp->segtype->name); "detected in your kernel", lp->segtype->name);
return 0; return 0;
} else if (!strcmp(lp->segtype->name, "raid10")) {
uint32_t maj, min, patchlevel;
if (!target_version("raid", &maj, &min, &patchlevel)) {
log_error("Failed to determine version of RAID kernel module");
return 0;
}
if ((maj != 1) || (min < 3)) {
log_error("RAID module does not support RAID10");
return 0;
}
} }
if (!_lvcreate_name_params(lp, cmd, &argc, &argv) || if (!_lvcreate_name_params(lp, cmd, &argc, &argv) ||

View File

@ -578,6 +578,7 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
seg_mirrors = 0; seg_mirrors = 0;
break; break;
} }
if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) { if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
log_print("Extending %" PRIu32 " mirror images.", log_print("Extending %" PRIu32 " mirror images.",
seg_mirrors); seg_mirrors);
@ -588,18 +589,26 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
log_error("Cannot vary number of mirrors in LV yet."); log_error("Cannot vary number of mirrors in LV yet.");
return EINVALID_CMD_LINE; return EINVALID_CMD_LINE;
} }
if (seg_mirrors && !strcmp(mirr_seg->segtype->name, "raid10")) {
lp->stripes = mirr_seg->area_count / seg_mirrors;
lp->stripe_size = mirr_seg->stripe_size;
}
} }
/* If extending, find stripes, stripesize & size of last segment */ /* If extending, find stripes, stripesize & size of last segment */
if ((lp->extents > lv->le_count) && if ((lp->extents > lv->le_count) &&
!(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) { !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size)) &&
strcmp(mirr_seg->segtype->name, "raid10")) {
/* FIXME Don't assume mirror seg will always be AREA_LV */ /* FIXME Don't assume mirror seg will always be AREA_LV */
/* FIXME We will need to support resize for metadata LV as well, /* FIXME We will need to support resize for metadata LV as well,
* and data LV could be any type (i.e. mirror)) */ * and data LV could be any type (i.e. mirror)) */
dm_list_iterate_items(seg, seg_mirrors ? &seg_lv(mirr_seg, 0)->segments : dm_list_iterate_items(seg, seg_mirrors ? &seg_lv(mirr_seg, 0)->segments :
lv_is_thin_pool(lv) ? &seg_lv(first_seg(lv), 0)->segments : &lv->segments) { lv_is_thin_pool(lv) ? &seg_lv(first_seg(lv), 0)->segments : &lv->segments) {
/* Allow through "striped" and RAID 4/5/6/10 */
if (!seg_is_striped(seg) && if (!seg_is_striped(seg) &&
(!seg_is_raid(seg) || seg_is_mirrored(seg))) (!seg_is_raid(seg) || seg_is_mirrored(seg)) &&
strcmp(seg->segtype->name, "raid10"))
continue; continue;
sz = seg->stripe_size; sz = seg->stripe_size;