]> sourceware.org Git - lvm2.git/commitdiff
Extend cling allocation policy to recognise PV tags (cling_by_tags).
authorAlasdair Kergon <agk@redhat.com>
Tue, 9 Nov 2010 12:34:40 +0000 (12:34 +0000)
committerAlasdair Kergon <agk@redhat.com>
Tue, 9 Nov 2010 12:34:40 +0000 (12:34 +0000)
Add allocation/cling_tag_list to lvm.conf.

12 files changed:
WHATS_NEW
doc/example.conf.in
lib/activate/activate.c
lib/datastruct/str_list.c
lib/datastruct/str_list.h
lib/display/display.c
lib/metadata/lv_manip.c
lib/metadata/metadata.c
lib/metadata/mirror.c
lib/metadata/vg.h
man/lvm.conf.5.in
tools/toollib.c

index ef0b775cc739412eeabd39b56454f1f3fcab5645..ce1d3b170e770e66319538241d47f0480e601dae 100644 (file)
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,7 @@
 Version 2.02.77 -
 ===================================
+  Extend cling allocation policy to recognise PV tags (cling_by_tags).
+  Add allocation/cling_tag_list to lvm.conf.
   Regenerate configure with 'autoreconf' for --enable-ocf. (2.02.76)
 
 Version 2.02.76 - 8th November 2010
index ac9aae707cc3c4ce3bc2d4883e368295c19afb19..8504f5dc0a47e1a22e34bdc908e72c6385f7abca 100644 (file)
@@ -146,6 +146,25 @@ devices {
     require_restorefile_with_uuid = 1
 }
 
+# This section allows you to configure the way in which LVM selects
+# free space for its Logical Volumes.
+#allocation {
+#    When searching for free space to extend an LV, the "cling"
+#    allocation policy will choose space on the same PVs as the last
+#    segment of the existing LV.  If there is insufficient space and a
+#    list of tags is defined here, it will check whether any of them are
+#    attached to the PVs concerned and then seek to match those PV tags
+#    between existing extents and new extents.
+#    Use the special tag "@*" as a wildcard to match any PV tag.
+#    
+#    Example: LVs are mirrored between two sites within a single VG.
+#    PVs are tagged with either @site1 or @site2 to indicate where
+#    they are situated.
+#
+#    cling_tag_list = [ "@site1", "@site2" ]
+#    cling_tag_list = [ "@*" ]
+#}
+
 # This section that allows you to configure the nature of the
 # information that LVM2 reports.
 log {
index 1a1315bd890ea0637b98f5bf3f5c84c464339e3e..d70544388eba623ec113db7f70b6e34153a79322 100644 (file)
@@ -275,8 +275,8 @@ static int _passes_activation_filter(struct cmd_context *cmd,
                        return 1;
 
                /* If any host tag matches any LV or VG tag, activate */
-               if (str_list_match_list(&cmd->tags, &lv->tags) ||
-                   str_list_match_list(&cmd->tags, &lv->vg->tags))
+               if (str_list_match_list(&cmd->tags, &lv->tags, NULL) ||
+                   str_list_match_list(&cmd->tags, &lv->vg->tags, NULL))
                        return 1;
 
                log_verbose("No host tag matches %s/%s",
@@ -314,9 +314,9 @@ static int _passes_activation_filter(struct cmd_context *cmd,
                        }
                        /* If any host tag matches any LV or VG tag, activate */
                        if (!strcmp(str, "*")) {
-                               if (str_list_match_list(&cmd->tags, &lv->tags)
+                               if (str_list_match_list(&cmd->tags, &lv->tags, NULL)
                                    || str_list_match_list(&cmd->tags,
-                                                          &lv->vg->tags))
+                                                          &lv->vg->tags, NULL))
                                            return 1;
                                else
                                        continue;
index a6f061ebbe732ab06544a05b24883c08926f0405..b7101489994bb3fbf9e37604557ef93a0e539a69 100644 (file)
@@ -93,14 +93,18 @@ int str_list_match_item(const struct dm_list *sll, const char *str)
 
 /*
  * Is at least one item on both lists?
+ * If tag_matched is non-NULL, it is set to the tag that matched.
  */
-int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2)
+int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2, char **tag_matched)
 {
        struct str_list *sl;
 
        dm_list_iterate_items(sl, sll)
-           if (str_list_match_item(sll2, sl->str))
-               return 1;
+               if (str_list_match_item(sll2, sl->str)) {
+                       if (tag_matched)
+                               *tag_matched = sl->str;
+                       return 1;
+               }
 
        return 0;
 }
index a3e634ef8eb53a86978d59fefb36040001362fc5..e145bfed2f19f3e41cf5558880a5221c3732fddd 100644 (file)
@@ -20,7 +20,7 @@ struct dm_list *str_list_create(struct dm_pool *mem);
 int str_list_add(struct dm_pool *mem, struct dm_list *sll, const char *str);
 int str_list_del(struct dm_list *sll, const char *str);
 int str_list_match_item(const struct dm_list *sll, const char *str);
-int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2);
+int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2, char **tag_matched);
 int str_list_lists_equal(const struct dm_list *sll, const struct dm_list *sll2);
 int str_list_dup(struct dm_pool *mem, struct dm_list *sllnew,
                 const struct dm_list *sllold);
index f1d4ff7401027b2f9a2c7467475e6224e035f293..2443a1023fc39b3d310195d054f435353b5cec99 100644 (file)
@@ -26,12 +26,13 @@ typedef enum { SIZE_LONG = 0, SIZE_SHORT = 1, SIZE_UNIT = 2 } size_len_t;
 
 static const struct {
        alloc_policy_t alloc;
-       const char str[12]; /* must be changed when size extends 11 chars */
+       const char str[14]; /* must be changed when size extends 13 chars */
        const char repchar;
 } _policies[] = {
        {
        ALLOC_CONTIGUOUS, "contiguous", 'c'}, {
        ALLOC_CLING, "cling", 'l'}, {
+       ALLOC_CLING_BY_TAGS, "cling_by_tags", 't'}, {   /* Only used in log mesgs */
        ALLOC_NORMAL, "normal", 'n'}, {
        ALLOC_ANYWHERE, "anywhere", 'a'}, {
        ALLOC_INHERIT, "inherit", 'i'}
@@ -147,12 +148,16 @@ alloc_policy_t get_alloc_from_string(const char *str)
 {
        int i;
 
+       /* cling_by_tags is part of cling */
+       if (!strcmp("cling_by_tags", str))
+               return ALLOC_CLING;
+
        for (i = 0; i < _num_policies; i++)
                if (!strcmp(_policies[i].str, str))
                        return _policies[i].alloc;
 
        /* Special case for old metadata */
-       if(!strcmp("next free", str))
+       if (!strcmp("next free", str))
                return ALLOC_NORMAL;
 
        log_error("Unrecognised allocation policy %s", str);
index 3c89ee4a7526dce678339deb205e52278e68a64c..1888b7c5d0798eb3f0c39c0886d0caa574216c66 100644 (file)
@@ -526,6 +526,8 @@ struct alloc_handle {
        uint32_t region_size;           /* Mirror region size */
        uint32_t total_area_len;        /* Total number of parallel extents */
 
+       const struct config_node *cling_tag_list_cn;
+
        struct dm_list *parallel_areas; /* PVs to avoid */
 
        /*
@@ -640,6 +642,8 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
 
        ah->parallel_areas = parallel_areas;
 
+       ah->cling_tag_list_cn = find_config_tree_node(cmd, "allocation/cling_tag_list");
+
        return ah;
 }
 
@@ -927,18 +931,19 @@ static int _comp_area(const void *l, const void *r)
  * Search for pvseg that matches condition
  */
 struct pv_match {
-       int (*condition)(struct pv_segment *pvseg, struct pv_area *pva);
+       int (*condition)(struct pv_match *pvmatch, struct pv_segment *pvseg, struct pv_area *pva);
 
        struct pv_area_used *areas;
        struct pv_area *pva;
        uint32_t areas_size;
+       const struct config_node *cling_tag_list_cn;
        int s;  /* Area index of match */
 };
 
 /*
  * Is PV area on the same PV?
  */
-static int _is_same_pv(struct pv_segment *pvseg, struct pv_area *pva)
+static int _is_same_pv(struct pv_match *pvmatch __attribute((unused)), struct pv_segment *pvseg, struct pv_area *pva)
 {
        if (pvseg->pv != pva->map->pv)
                return 0;
@@ -946,10 +951,71 @@ static int _is_same_pv(struct pv_segment *pvseg, struct pv_area *pva)
        return 1;
 }
 
+/*
+ * Does PV area have a tag listed in allocation/cling_tag_list that 
+ * matches a tag of the PV of the existing segment?
+ */
+static int _has_matching_pv_tag(struct pv_match *pvmatch, struct pv_segment *pvseg, struct pv_area *pva)
+{
+       struct config_value *cv;
+       char *str;
+       char *tag_matched;
+
+       for (cv = pvmatch->cling_tag_list_cn->v; cv; cv = cv->next) {
+               if (cv->type != CFG_STRING) {
+                       log_error("Ignoring invalid string in config file entry "
+                                 "allocation/cling_tag_list");
+                       continue;
+               }
+               str = cv->v.str;
+               if (!*str) {
+                       log_error("Ignoring empty string in config file entry "
+                                 "allocation/cling_tag_list");
+                       continue;
+               }
+
+               if (*str != '@') {
+                       log_error("Ignoring string not starting with @ in config file entry "
+                                 "allocation/cling_tag_list: %s", str);
+                       continue;
+               }
+
+               str++;
+
+               if (!*str) {
+                       log_error("Ignoring empty tag in config file entry "
+                                 "allocation/cling_tag_list");
+                       continue;
+               }
+
+               /* Wildcard matches any tag against any tag. */
+               if (!strcmp(str, "*")) {
+                       if (!str_list_match_list(&pvseg->pv->tags, &pva->map->pv->tags, &tag_matched))
+                               continue;
+                       else {
+                               log_debug("Matched allocation PV tag %s on existing %s with free space on %s.",
+                                         tag_matched, pv_dev_name(pvseg->pv), pv_dev_name(pva->map->pv));
+                               return 1;
+                       }
+               }
+
+               if (!str_list_match_item(&pvseg->pv->tags, str) ||
+                   !str_list_match_item(&pva->map->pv->tags, str))
+                       continue;
+               else {
+                       log_debug("Matched allocation PV tag %s on existing %s with free space on %s.",
+                                 str, pv_dev_name(pvseg->pv), pv_dev_name(pva->map->pv));
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Is PV area contiguous to PV segment?
  */
-static int _is_contiguous(struct pv_segment *pvseg, struct pv_area *pva)
+static int _is_contiguous(struct pv_match *pvmatch __attribute((unused)), struct pv_segment *pvseg, struct pv_area *pva)
 {
        if (pvseg->pv != pva->map->pv)
                return 0;
@@ -966,7 +1032,7 @@ static int _is_condition(struct cmd_context *cmd __attribute__((unused)),
 {
        struct pv_match *pvmatch = data;
 
-       if (!pvmatch->condition(pvseg, pvmatch->pva))
+       if (!pvmatch->condition(pvmatch, pvseg, pvmatch->pva))
                return 1;       /* Continue */
 
        if (s >= pvmatch->areas_size)
@@ -991,16 +1057,18 @@ static int _is_condition(struct cmd_context *cmd __attribute__((unused)),
  * Is pva on same PV as any existing areas?
  */
 static int _check_cling(struct cmd_context *cmd,
+                       const struct config_node *cling_tag_list_cn,
                        struct lv_segment *prev_lvseg, struct pv_area *pva,
                        struct pv_area_used *areas, uint32_t areas_size)
 {
        struct pv_match pvmatch;
        int r;
 
-       pvmatch.condition = _is_same_pv;
+       pvmatch.condition = cling_tag_list_cn ? _has_matching_pv_tag : _is_same_pv;
        pvmatch.areas = areas;
        pvmatch.areas_size = areas_size;
        pvmatch.pva = pva;
+       pvmatch.cling_tag_list_cn = cling_tag_list_cn;
 
        /* FIXME Cope with stacks by flattening */
        if (!(r = _for_each_pv(cmd, prev_lvseg->lv,
@@ -1029,6 +1097,7 @@ static int _check_contiguous(struct cmd_context *cmd,
        pvmatch.areas = areas;
        pvmatch.areas_size = areas_size;
        pvmatch.pva = pva;
+       pvmatch.cling_tag_list_cn = NULL;
 
        /* FIXME Cope with stacks by flattening */
        if (!(r = _for_each_pv(cmd, prev_lvseg->lv,
@@ -1056,7 +1125,7 @@ static int _find_parallel_space(struct alloc_handle *ah, alloc_policy_t alloc,
        struct pv_area *pva;
        struct pv_list *pvl;
        unsigned already_found_one = 0;
-       unsigned contiguous = 0, cling = 0, preferred_count = 0;
+       unsigned contiguous = 0, cling = 0, use_cling_tags = 0, preferred_count = 0;
        unsigned ix, last_ix;
        unsigned ix_offset = 0; /* Offset for non-preferred allocations */
        unsigned ix_log_offset; /* Offset to start of areas to use for log */
@@ -1089,7 +1158,10 @@ static int _find_parallel_space(struct alloc_handle *ah, alloc_policy_t alloc,
                        contiguous = 1;
                else if ((alloc == ALLOC_CLING))
                        cling = 1;
-               else
+               else if ((alloc == ALLOC_CLING_BY_TAGS)) {
+                       cling = 1;
+                       use_cling_tags = 1;
+               } else
                        ix_offset = 0;
        }
 
@@ -1176,9 +1248,10 @@ static int _find_parallel_space(struct alloc_handle *ah, alloc_policy_t alloc,
                                        if (cling) {
                                                if (prev_lvseg &&
                                                    _check_cling(ah->cmd,
-                                                                  prev_lvseg,
-                                                                  pva, *areas_ptr,
-                                                                  *areas_size_ptr)) {
+                                                                use_cling_tags ? ah->cling_tag_list_cn : NULL,
+                                                                prev_lvseg,
+                                                                pva, *areas_ptr,
+                                                                *areas_size_ptr)) {
                                                        preferred_count++;
                                                }
                                                goto next_pv;
@@ -1361,8 +1434,18 @@ static int _allocate(struct alloc_handle *ah,
                return 0;
        }
 
+       /*
+        * cling includes implicit cling_by_tags
+        * but it does nothing unless the lvm.conf setting is present.
+        */
+       if (ah->alloc == ALLOC_CLING)
+               ah->alloc = ALLOC_CLING_BY_TAGS;
+
        /* Attempt each defined allocation policy in turn */
        for (alloc = ALLOC_CONTIGUOUS; alloc < ALLOC_INHERIT; alloc++) {
+               /* Skip cling_by_tags if no list defined */
+               if (alloc == ALLOC_CLING_BY_TAGS && !ah->cling_tag_list_cn)
+                       continue;
                old_allocated = allocated;
                log_debug("Trying allocation using %s policy.  "
                          "Need %" PRIu32 " extents for %" PRIu32 " parallel areas and %" PRIu32 " log areas of %" PRIu32 " extents. "
@@ -1829,8 +1912,8 @@ static int _rename_sub_lv(struct cmd_context *cmd,
        /*
         * Compose a new name for sub lv:
         *   e.g. new name is "lvol1_mlog"
-        *        if the sub LV is "lvol0_mlog" and
-        *        a new name for main LV is "lvol1"
+        *      if the sub LV is "lvol0_mlog" and
+        *      a new name for main LV is "lvol1"
         */
        len = strlen(lv_name_new) + strlen(suffix) + 1;
        new_name = dm_pool_alloc(cmd->mem, len);
@@ -2339,7 +2422,7 @@ int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume *
                }
        }
 
-        return lv_remove_single(cmd, lv, force);
+       return lv_remove_single(cmd, lv, force);
 }
 
 /*
index e81f87fc7320414941c7882a15e0913aafb9cb30..61b4600633fb30ea37ce3b3d25093a96207dbf54 100644 (file)
@@ -2164,6 +2164,12 @@ int vg_validate(struct volume_group *vg)
        uint32_t num_snapshots = 0;
        uint32_t loop_counter1, loop_counter2;
 
+       if (vg->alloc == ALLOC_CLING_BY_TAGS) {
+               log_error(INTERNAL_ERROR "VG %s allocation policy set to invalid cling_by_tags.",
+                         vg->name);
+               r = 0;
+       }
+
        /* FIXME Also check there's no data/metadata overlap */
        dm_list_iterate_items(pvl, &vg->pvs) {
                if (++pv_count > vg->pv_count) {
@@ -2233,6 +2239,12 @@ int vg_validate(struct volume_group *vg)
                        r = 0;
                }
 
+               if (lvl->lv->alloc == ALLOC_CLING_BY_TAGS) {
+                       log_error(INTERNAL_ERROR "LV %s allocation policy set to invalid cling_by_tags.",
+                                 lvl->lv->name);
+                       r = 0;
+               }
+
                if (lvl->lv->status & VISIBLE_LV)
                        continue;
 
index 6224132ea143ed301b3ea7bd57db3b9e42307c4c..d5f107a7967af989ad3dd8cee8eaaa03fd980e38 100644 (file)
@@ -400,7 +400,7 @@ static int _delete_lv(struct logical_volume *mirror_lv, struct logical_volume *l
        struct str_list *sl;
 
        /* Inherit tags - maybe needed for activation */
-       if (!str_list_match_list(&mirror_lv->tags, &lv->tags)) {
+       if (!str_list_match_list(&mirror_lv->tags, &lv->tags, NULL)) {
                dm_list_iterate_items(sl, &mirror_lv->tags)
                        if (!str_list_add(cmd->mem, &lv->tags, sl->str)) {
                                log_error("Aborting. Unable to tag.");
index 39c894a66f7eeb3b0361966888d0996f2fa5196b..eb9debccef4f3f79b54460b75ee09d9940c8873e 100644 (file)
@@ -25,6 +25,7 @@ typedef enum {
        ALLOC_INVALID,
        ALLOC_CONTIGUOUS,
        ALLOC_CLING,
+       ALLOC_CLING_BY_TAGS,    /* Internal - never written or displayed. */
        ALLOC_NORMAL,
        ALLOC_ANYWHERE,
        ALLOC_INHERIT
index 44529bf6108cc3735aa936988611cb2f62cea522..ac16b1d59792ad28c621147882138003b5bb1c30 100644 (file)
@@ -172,6 +172,28 @@ the limit set here, no further I/O is sent to that device for the remainder of
 the respective operation. Setting the parameter to 0 disables the counters
 altogether.
 .TP
+\fBallocation\fP \(em Space allocation policies
+.IP
+\fBcling_tag_list\fP \(em List of PV tags matched by the \fBcling\fP allocation policy.
+.IP
+When searching for free space to extend an LV, the \fBcling\fP
+allocation policy will choose space on the same PVs as the last
+segment of the existing LV.  If there is insufficient space and a
+list of tags is defined here, it will check whether any of them are
+attached to the PVs concerned and then seek to match those PV tags
+between existing extents and new extents.
+.IP 
+The @ prefix for tags is required.
+Use the special tag "@*" as a wildcard to match any PV tag and so use 
+all PV tags for this purpose.
+.IP
+For example, LVs are mirrored between two sites within a single VG.
+PVs are tagged with either @site1 or @site2 to indicate where
+they are situated and these two PV tags are selected for use with this
+allocation policy:
+.IP
+cling_tag_list = [ "@site1", "@site2" ]
+.TP
 \fBlog\fP \(em Default log settings
 .IP
 \fBfile\fP \(em Location of log file.  If this entry is not present, no
index 5adc160c05cc0c286f7c36492c56b71d5959ee9d..1e993526761ffcbb7fdb1f456386914ffdf4d738 100644 (file)
@@ -115,7 +115,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd,
 
        /* Or if VG tags match */
        if (!process_lv && tags_supplied &&
-           str_list_match_list(tags, &vg->tags)) {
+           str_list_match_list(tags, &vg->tags, NULL)) {
                process_all = 1;
        }
 
@@ -141,7 +141,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd,
 
                /* LV tag match? */
                if (!process_lv && tags_supplied &&
-                   str_list_match_list(tags, &lvl->lv->tags)) {
+                   str_list_match_list(tags, &lvl->lv->tags, NULL)) {
                        process_lv = 1;
                }
 
@@ -487,7 +487,7 @@ static int _process_one_vg(struct cmd_context *cmd, const char *vg_name,
                if (!dm_list_empty(tags) &&
                    /* Only process if a tag matches or it's on arg_vgnames */
                    !str_list_match_item(arg_vgnames, vg_name) &&
-                   !str_list_match_list(tags, &cvl_vg->vg->tags))
+                   !str_list_match_list(tags, &cvl_vg->vg->tags, NULL))
                        break;
 
                ret = process_single_vg(cmd, vg_name, cvl_vg->vg, handle);
@@ -606,7 +606,7 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
 
        dm_list_iterate_items(pvl, &vg->pvs) {
                if (tags && !dm_list_empty(tags) &&
-                   !str_list_match_list(tags, &pvl->pv->tags)) {
+                   !str_list_match_list(tags, &pvl->pv->tags, NULL)) {
                        continue;
                }
                if ((ret = process_single_pv(cmd, vg, pvl->pv, handle)) > ret_max)
This page took 0.059387 seconds and 5 git commands to generate.