1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-10-27 01:55:10 +03:00

Extend cling allocation policy to recognise PV tags (cling_by_tags).

Add allocation/cling_tag_list to lvm.conf.
This commit is contained in:
Alasdair Kergon 2010-11-09 12:34:40 +00:00
parent 5a976864ee
commit eb82bd0525
12 changed files with 176 additions and 28 deletions

View File

@ -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

View 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 {

View 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;

View 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))
if (str_list_match_item(sll2, sl->str)) {
if (tag_matched)
*tag_matched = sl->str;
return 1;
}
return 0;
}

View 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);

View 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,6 +148,10 @@ 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;

View 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,6 +1248,7 @@ static int _find_parallel_space(struct alloc_handle *ah, alloc_policy_t alloc,
if (cling) {
if (prev_lvseg &&
_check_cling(ah->cmd,
use_cling_tags ? ah->cling_tag_list_cn : NULL,
prev_lvseg,
pva, *areas_ptr,
*areas_size_ptr)) {
@ -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. "

View 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;

View 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.");

View 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

View 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

View 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)