[lvm-devel] [PATCH 2/6] RAID: Support moving RAID sub-LVs with pvmove
Jonathan Brassow
jbrassow at redhat.com
Mon Aug 5 20:03:13 UTC 2013
This patch allows pvmove to operate on RAID logical volumes.
The key component is the ability to avoid moving a RAID
sub-LV onto a PV that already has another RAID sub-LV on it.
(e.g. Avoid placing both images of a RAID1 LV on the same PV.)
Top-level LVs are processed to determine which PVs to avoid for
the sake of redundancy, while bottom-level LVs are processed
to determine which segments/extents to move.
---
tools/pvmove.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 98 insertions(+), 13 deletions(-)
diff --git a/tools/pvmove.c b/tools/pvmove.c
index cb5c9ec..64db144 100644
--- a/tools/pvmove.c
+++ b/tools/pvmove.c
@@ -16,6 +16,7 @@
#include "tools.h"
#include "polldaemon.h"
#include "display.h"
+#include "metadata-exported.h" /* for 'get_only_segment_using_this_lv' */
#define PVMOVE_FIRST_TIME 0x00000001 /* Called for first time */
#define PVMOVE_EXCLUSIVE 0x00000002 /* Require exclusive LV */
@@ -135,6 +136,47 @@ static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc,
}
/*
+ * _trim_allocatable_pvs
+ * @alloc_list
+ * @trim_list
+ *
+ * Remove PVs in 'trim_list' from 'alloc_list'.
+ *
+ * Returns: 1 on success, 0 on error
+ */
+static int _trim_allocatable_pvs(struct dm_list *alloc_list,
+ struct dm_list *trim_list,
+ alloc_policy_t alloc)
+{
+ struct dm_list *pvht, *pvh, *trim_pvh;
+ struct pv_list *pvl, *trim_pvl;
+
+ if (!alloc_list) {
+ log_error(INTERNAL_ERROR "alloc_list is NULL");
+ return 0;
+ }
+
+ if (!trim_list || dm_list_empty(trim_list))
+ return 1; /* alloc_list stays the same */
+
+ dm_list_iterate_safe(pvh, pvht, alloc_list) {
+ pvl = dm_list_item(pvh, struct pv_list);
+
+ dm_list_iterate(trim_pvh, trim_list) {
+ trim_pvl = dm_list_item(trim_pvh, struct pv_list);
+
+ /* Don't allocate onto a trim PV */
+ if ((alloc != ALLOC_ANYWHERE) &&
+ (pvl->pv == trim_pvl->pv)) {
+ dm_list_del(&pvl->list);
+ break; /* goto next in alloc_list */
+ }
+ }
+ }
+ return 1;
+}
+
+/*
* Replace any LV segments on given PV with temporary mirror.
* Returns list of LVs changed.
*/
@@ -181,6 +223,8 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
struct logical_volume *lv_mirr, *lv;
struct lv_segment *seg;
struct lv_list *lvl;
+ struct pv_list *pvl, *tmp_pvl;
+ struct dm_list trim_list;
uint32_t log_count = 0;
int lv_found = 0;
int lv_skipped = 0;
@@ -204,7 +248,48 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
dm_list_init(*lvs_changed);
- /* Find segments to be moved and set up mirrors */
+ /*
+ * First,
+ * use top-level LVs (like RAID, mirror, or thin)
+ * to build a list of PVs that must be avoided due to
+ * the need to maintain redundancy (e.g. no two RAID images
+ * should be placed on the same device - unless ALLOC_ANYWHERE).
+ */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+ if (lv == lv_mirr)
+ continue;
+
+ if (lv_name && strcmp(lv->name, lv_name))
+ continue;
+
+ if (!lv_is_on_pvs(lv, source_pvl))
+ continue;
+
+ dm_list_init(&trim_list);
+
+ if (seg_is_raid(first_seg(lv))) {
+ if (!get_pv_list_for_lv(lv->vg->cmd->mem,
+ lv, &trim_list))
+ return_NULL;
+
+ if (!_trim_allocatable_pvs(allocatable_pvs,
+ &trim_list, alloc))
+ return_NULL;
+
+ /* Free list obtained from 'get_pv_list_for_lv' */
+ dm_list_iterate_items_safe(pvl, tmp_pvl, &trim_list) {
+ dm_list_del(&pvl->list);
+ dm_pool_free(lv->vg->cmd->mem, pvl);
+ }
+ }
+ }
+
+ /*
+ * Second,
+ * use bottom-level LVs (like *_mimage_*, *_mlog, *_rmeta_*, etc)
+ * to find segments to be moved and then set up mirrors.
+ */
dm_list_iterate_items(lvl, &vg->lvs) {
lv = lvl->lv;
if (lv == lv_mirr)
@@ -214,23 +299,23 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
continue;
lv_found = 1;
}
+
+ if (!lv_is_on_pvs(lv, source_pvl))
+ continue;
+
if (lv_is_origin(lv) || lv_is_cow(lv)) {
lv_skipped = 1;
log_print_unless_silent("Skipping snapshot-related LV %s", lv->name);
continue;
}
- if (lv_is_raid_type(lv)) {
- seg = first_seg(lv);
- if (seg_is_raid(seg)) {
- lv_skipped = 1;
- log_print_unless_silent("Skipping %s LV %s",
- seg->segtype->ops->name(seg),
- lv->name);
- continue;
- }
- lv_skipped = 1;
- log_print_unless_silent("Skipping RAID sub-LV %s",
- lv->name);
+ seg = first_seg(lv);
+ if (seg_is_raid(seg)) {
+ /*
+ * Pass over top-level LVs - they were handled.
+ * Allow sub-LVs to proceed.
+ */
+ log_debug("Ignoring top-level %s LV, %s",
+ seg->segtype->ops->name(seg), lv->name);
continue;
}
if (lv->status & MIRRORED) {
--
1.7.7.6
More information about the lvm-devel
mailing list