master - thin: calculate thin pool chunk size based on device IO hints
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=78cba8eb3fa262...
Commit: 78cba8eb3fa262c2c4e1a5c5a3db3e4e0d7e6900
Parent: cc9e65c391f9070c4b06a94a0ffdb9c61cf89c13
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Wed Sep 25 16:01:47 2013 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Wed Sep 25 16:06:38 2013 +0200
thin: calculate thin pool chunk size based on device IO hints
If "default" thin pool chunk size calculation method is selected,
use minimum_io_size, otherwise optimal_io_size for "performance"
device hint exposed in sysfs. If there appear to be PVs with
different hints presented, use their least common multiple.
If the hint is less than the default value defined for the
calculation method, use the default value instead.
---
lib/metadata/lv_manip.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 68 insertions(+), 1 deletions(-)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index d1ec4cd..f945b79 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -5510,6 +5510,72 @@ int lv_activation_skip(struct logical_volume *lv, activation_change_t activate,
return (lv->status & LV_ACTIVATION_SKIP) ? 1 : 0;
}
+/* Greatest common divisor */
+static unsigned long _gcd(unsigned long n1, unsigned long n2)
+{
+ unsigned long remainder;
+
+ do {
+ remainder = n1 % n2;
+ n1 = n2;
+ n2 = remainder;
+ } while (n2);
+
+ return n1;
+}
+
+/* Least common multiple */
+static unsigned long _lcm(unsigned long n1, unsigned long n2)
+{
+ if (!n1 || !n2)
+ return 0;
+ return (n1 * n2) / _gcd(n1, n2);
+}
+
+static int _recalculate_thin_pool_chunk_size_with_dev_hints(struct lvcreate_params *lp,
+ struct logical_volume *pool_lv)
+{
+ struct logical_volume *pool_data_lv;
+ struct lv_segment *seg;
+ struct physical_volume *pv;
+ struct cmd_context *cmd = pool_lv->vg->cmd;
+ unsigned long previous_hint = 0, hint;
+ uint32_t chunk_size = lp->chunk_size;
+ uint32_t default_chunk_size = lp->thin_chunk_size_calc_method == THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE ?
+ DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE*2 : DEFAULT_THIN_POOL_CHUNK_SIZE*2;
+
+ if (lp->passed_args & PASS_ARG_CHUNK_SIZE ||
+ find_config_tree_int(cmd, allocation_thin_pool_chunk_size_CFG, NULL))
+ goto out;
+
+ pool_data_lv = seg_lv(first_seg(pool_lv), 0);
+
+ dm_list_iterate_items(seg, &pool_data_lv->segments) {
+ pv = seg_pv(seg, 0);
+ if (lp->thin_chunk_size_calc_method == THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE)
+ hint = dev_optimal_io_size(cmd->dev_types, pv_dev(pv));
+ else
+ hint = dev_minimum_io_size(cmd->dev_types, pv_dev(pv));
+
+ if (!hint)
+ continue;
+ if (previous_hint)
+ hint = _lcm(previous_hint, hint);
+ previous_hint = hint;
+ }
+
+ if (hint < DM_THIN_MIN_DATA_BLOCK_SIZE ||
+ hint > DM_THIN_MAX_DATA_BLOCK_SIZE) {
+ log_debug_alloc("Calculated value of %ld sectors for thin pool "
+ "chunk size is out of allowed range (%d-%d).",
+ hint, DM_THIN_MIN_DATA_BLOCK_SIZE, DM_THIN_MAX_DATA_BLOCK_SIZE);
+ } else
+ chunk_size = hint >= default_chunk_size ? hint : default_chunk_size;
+out:
+ first_seg(pool_lv)->chunk_size = chunk_size;
+ return 1;
+}
+
/* Thin notes:
* If lp->thin OR lp->activate is AY*, activate the pool if not already active.
* If lp->thin, create thin LV within the pool - as a snapshot if lp->snapshot.
@@ -5808,8 +5874,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
return_NULL;
if (seg_is_thin_pool(lp)) {
+ if (!_recalculate_thin_pool_chunk_size_with_dev_hints(lp, lv))
+ return_NULL;
first_seg(lv)->zero_new_blocks = lp->zero ? 1 : 0;
- first_seg(lv)->chunk_size = lp->chunk_size;
first_seg(lv)->discards = lp->discards;
/* FIXME: use lowwatermark via lvm.conf global for all thinpools ? */
first_seg(lv)->low_water_mark = 0;
10 years, 6 months
master - thin: use appropriate default value based on allocation/thin_pool_chunk_size_calculation setting
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=cc9e65c391f907...
Commit: cc9e65c391f9070c4b06a94a0ffdb9c61cf89c13
Parent: 8bf425005c8a25e9b99a617a6cf7d9d826ad5f6a
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Wed Sep 25 16:00:52 2013 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Wed Sep 25 16:06:38 2013 +0200
thin: use appropriate default value based on allocation/thin_pool_chunk_size_calculation setting
If thin_pool_chunk_size_calculation is set to "default", use 64KiB,
otheriwse 512KiB for "performance".
---
lib/config/config_settings.h | 2 +-
lib/config/defaults.h | 1 +
lib/metadata/metadata-exported.h | 11 ++++++--
lib/metadata/thin_manip.c | 48 ++++++++++++++++++++++++++++++--------
tools/lvconvert.c | 15 ++++++-----
tools/lvcreate.c | 2 +
tools/toollib.c | 4 ++-
tools/toollib.h | 1 +
8 files changed, 62 insertions(+), 22 deletions(-)
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 8ea2c92..f7c1330 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -108,7 +108,7 @@ cfg(allocation_thin_pool_metadata_require_separate_pvs_CFG, "thin_pool_metadata_
cfg(allocation_thin_pool_zero_CFG, "thin_pool_zero", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_ZERO, vsn(2, 2, 99), NULL)
cfg(allocation_thin_pool_discards_CFG, "thin_pool_discards", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_STRING, DEFAULT_THIN_POOL_DISCARDS, vsn(2, 2, 99), NULL)
cfg(allocation_thin_pool_chunk_size_calculation_CFG, "thin_pool_chunk_size_calculation", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_STRING, DEFAULT_THIN_POOL_CHUNK_SIZE_CALCULATION, vsn(2, 2, 101), NULL)
-cfg(allocation_thin_pool_chunk_size_CFG, "thin_pool_chunk_size", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_INT, DEFAULT_THIN_POOL_CHUNK_SIZE, vsn(2, 2, 99), NULL)
+cfg(allocation_thin_pool_chunk_size_CFG, "thin_pool_chunk_size", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_INT, 0, vsn(2, 2, 99), NULL)
cfg(log_verbose_CFG, "verbose", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_VERBOSE, vsn(1, 0, 0), NULL)
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index 361d927..5aacea1 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -73,6 +73,7 @@
#define DEFAULT_THIN_POOL_OPTIMAL_SIZE (128 * 1024 * 1024) /* KB */
#define DEFAULT_THIN_POOL_CHUNK_SIZE_CALCULATION "default"
#define DEFAULT_THIN_POOL_CHUNK_SIZE 64 /* KB */
+#define DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE 512 /* KB */
#define DEFAULT_THIN_POOL_DISCARDS "passdown"
#define DEFAULT_THIN_POOL_ZERO 1
#define DEFAULT_POOL_METADATA_SPARE 1
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 3920385..71fe393 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -659,11 +659,13 @@ int pool_is_active(const struct logical_volume *pool_lv);
int pool_can_resize_metadata(const struct logical_volume *pool_lv);
int update_pool_lv(struct logical_volume *lv, int activate);
int update_profilable_pool_params(struct cmd_context *cmd, struct profile *profile,
- int passed_args, uint32_t *chunk_size,
- thin_discards_t *discards, int *zero);
+ int passed_args, int *chunk_size_calc_method,
+ uint32_t *chunk_size, thin_discards_t *discards,
+ int *zero);
int update_pool_params(struct volume_group *vg, unsigned attr, int passed_args,
uint32_t data_extents, uint32_t extent_size,
- uint32_t *chunk_size, thin_discards_t *discards,
+ int *chunk_size_calc_method, uint32_t *chunk_size,
+ thin_discards_t *discards,
uint64_t *pool_metadata_size, int *zero);
int get_pool_discards(const char *str, thin_discards_t *discards);
const char *get_pool_discards_name(thin_discards_t discards);
@@ -724,6 +726,9 @@ struct lvcreate_params {
int activation_skip; /* activation skip flags */
activation_change_t activate; /* non-snapshot, non-mirror */
thin_discards_t discards; /* thin */
+#define THIN_CHUNK_SIZE_CALC_METHOD_DEFAULT 0x01
+#define THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE 0x02
+ int thin_chunk_size_calc_method;
const char *origin; /* snap */
const char *pool; /* thin */
diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c
index 971ffe6..c71b971 100644
--- a/lib/metadata/thin_manip.c
+++ b/lib/metadata/thin_manip.c
@@ -569,14 +569,40 @@ int update_pool_lv(struct logical_volume *lv, int activate)
return 1;
}
+static int _get_pool_chunk_size_calc(const char *str,
+ int *chunk_size_calc_method,
+ uint32_t *chunk_size)
+{
+ if (!strcasecmp(str, "default")) {
+ *chunk_size_calc_method = THIN_CHUNK_SIZE_CALC_METHOD_DEFAULT;
+ *chunk_size = DEFAULT_THIN_POOL_CHUNK_SIZE * 2;
+ }
+ else if (!strcasecmp(str, "performance")) {
+ *chunk_size_calc_method = THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE;
+ *chunk_size = DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE * 2;
+ }
+ else {
+ log_error("Thin pool chunk size calculation method \"%s\" is unknown.", str);
+ return 0;
+ }
+
+ return 1;
+}
+
int update_profilable_pool_params(struct cmd_context *cmd, struct profile *profile,
- int passed_args, uint32_t *chunk_size,
- thin_discards_t *discards, int *zero)
+ int passed_args, int *chunk_size_calc_method,
+ uint32_t *chunk_size, thin_discards_t *discards,
+ int *zero)
{
- const char *dstr;
+ const char *str;
- if (!(passed_args & PASS_ARG_CHUNK_SIZE))
- *chunk_size = find_config_tree_int(cmd, allocation_thin_pool_chunk_size_CFG, profile) * 2;
+ if (!(passed_args & PASS_ARG_CHUNK_SIZE)) {
+ if (!(*chunk_size = find_config_tree_int(cmd, allocation_thin_pool_chunk_size_CFG, profile) * 2)) {
+ str = find_config_tree_str(cmd, allocation_thin_pool_chunk_size_calculation_CFG, profile);
+ if (!_get_pool_chunk_size_calc(str, chunk_size_calc_method, chunk_size))
+ return_0;
+ }
+ }
if ((*chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) ||
(*chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE)) {
@@ -587,8 +613,8 @@ int update_profilable_pool_params(struct cmd_context *cmd, struct profile *profi
}
if (!(passed_args & PASS_ARG_DISCARDS)) {
- dstr = find_config_tree_str(cmd, allocation_thin_pool_discards_CFG, profile);
- if (!get_pool_discards(dstr, discards))
+ str = find_config_tree_str(cmd, allocation_thin_pool_discards_CFG, profile);
+ if (!get_pool_discards(str, discards))
return_0;
}
@@ -600,14 +626,16 @@ int update_profilable_pool_params(struct cmd_context *cmd, struct profile *profi
int update_pool_params(struct volume_group *vg, unsigned attr, int passed_args,
uint32_t data_extents, uint32_t extent_size,
- uint32_t *chunk_size, thin_discards_t *discards,
- uint64_t *pool_metadata_size, int *zero)
+ int *chunk_size_calc_method, uint32_t *chunk_size,
+ thin_discards_t *discards, uint64_t *pool_metadata_size,
+ int *zero)
{
size_t estimate_chunk_size;
struct cmd_context *cmd = vg->cmd;
if (!update_profilable_pool_params(cmd, vg->profile, passed_args,
- chunk_size, discards, zero))
+ chunk_size_calc_method, chunk_size,
+ discards, zero))
return_0;
if (!(attr & THIN_FEATURE_BLOCK_SIZE) &&
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 2a3c827..bbcfff7 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -34,6 +34,7 @@ struct lvconvert_params {
int wait_completion;
int need_polling;
+ int thin_chunk_size_calc_method;
uint32_t chunk_size;
uint32_t region_size;
@@ -2328,8 +2329,8 @@ static int _lvconvert_thinpool(struct cmd_context *cmd,
if (!lp->pool_metadata_lv_name) {
if (!update_pool_params(pool_lv->vg, lp->target_attr, lp->passed_args,
pool_lv->le_count, pool_lv->vg->extent_size,
- &lp->chunk_size, &lp->discards,
- &lp->poolmetadata_size, &lp->zero))
+ &lp->thin_chunk_size_calc_method, &lp->chunk_size,
+ &lp->discards, &lp->poolmetadata_size, &lp->zero))
return_0;
if (!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size))
@@ -2440,8 +2441,8 @@ static int _lvconvert_thinpool(struct cmd_context *cmd,
}
if (!update_pool_params(pool_lv->vg, lp->target_attr, lp->passed_args,
pool_lv->le_count, pool_lv->vg->extent_size,
- &lp->chunk_size, &lp->discards,
- &lp->poolmetadata_size, &lp->zero))
+ &lp->thin_chunk_size_calc_method, &lp->chunk_size,
+ &lp->discards, &lp->poolmetadata_size, &lp->zero))
return_0;
}
@@ -2710,9 +2711,9 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp
if (arg_count(cmd, thinpool_ARG) &&
!get_pool_params(cmd, lv_config_profile(lv),
- &lp->passed_args, &lp->chunk_size,
- &lp->discards, &lp->poolmetadata_size,
- &lp->zero))
+ &lp->passed_args, &lp->thin_chunk_size_calc_method,
+ &lp->chunk_size, &lp->discards,
+ &lp->poolmetadata_size, &lp->zero))
goto_bad;
/*
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
index 095adbb..c5a2e20 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -336,6 +336,7 @@ static int _update_extents_params(struct volume_group *vg,
if (lp->create_thin_pool) {
if (!update_pool_params(vg, lp->target_attr, lp->passed_args,
lp->extents, vg->extent_size,
+ &lp->thin_chunk_size_calc_method,
&lp->chunk_size, &lp->discards,
&lp->poolmetadatasize, &lp->zero))
return_0;
@@ -876,6 +877,7 @@ static int _lvcreate_params(struct lvcreate_params *lp,
!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size) ||
(lp->create_thin_pool &&
!get_pool_params(cmd, NULL, &lp->passed_args,
+ &lp->thin_chunk_size_calc_method,
&lp->chunk_size, &lp->discards,
&lp->poolmetadatasize, &lp->zero)) ||
!_read_mirror_params(lp, cmd) ||
diff --git a/tools/toollib.c b/tools/toollib.c
index 099e82b..9b3e842 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1631,6 +1631,7 @@ int get_activation_monitoring_mode(struct cmd_context *cmd,
int get_pool_params(struct cmd_context *cmd,
struct profile *profile,
int *passed_args,
+ int *chunk_size_calc_method,
uint32_t *chunk_size,
thin_discards_t *discards,
uint64_t *pool_metadata_size,
@@ -1663,7 +1664,8 @@ int get_pool_params(struct cmd_context *cmd,
}
if (!update_profilable_pool_params(cmd, profile, *passed_args,
- chunk_size, discards, zero))
+ chunk_size_calc_method, chunk_size,
+ discards, zero))
return_0;
if (arg_count(cmd, poolmetadatasize_ARG)) {
diff --git a/tools/toollib.h b/tools/toollib.h
index cd9ca49..d831b95 100644
--- a/tools/toollib.h
+++ b/tools/toollib.h
@@ -119,6 +119,7 @@ int get_activation_monitoring_mode(struct cmd_context *cmd,
int get_pool_params(struct cmd_context *cmd,
struct profile *profile,
int *passed_args,
+ int *chunk_size_calc_method,
uint32_t *chunk_size,
thin_discards_t *discards,
uint64_t *pool_metadata_size,
10 years, 6 months
master - conf: add allocation/thin_pool_chunk_size_calculation
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=8bf425005c8a25...
Commit: 8bf425005c8a25e9b99a617a6cf7d9d826ad5f6a
Parent: c37c59e155813545c2e674eb370a4609e97aa769
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Wed Sep 25 15:59:30 2013 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Wed Sep 25 16:06:38 2013 +0200
conf: add allocation/thin_pool_chunk_size_calculation
Add allocation/thin_pool_chunk_size_calculation lvm.conf
option to select a method for calculating thin pool chunk
sizes and define two possible values - "default" and "performance".
---
WHATS_NEW | 1 +
conf/example.conf.in | 15 +++++++++++++++
lib/config/config_settings.h | 2 ++
lib/config/defaults.h | 1 +
4 files changed, 19 insertions(+), 0 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 8a50122..04906f0 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.103 -
======================================
+ Add lvm.conf allocation/thin_pool_chunk_size_calculation option.
Fix contiguous & cling allocation policies for parity RAID. (2.02.100)
Set use_lvmetad=0 on lvmconf --enable-cluster, reset to default on --disable-cluster.
Don't install separate command symlink in binary directory for 'lvm devtypes'.
diff --git a/conf/example.conf.in b/conf/example.conf.in
index 6adcecf..f06b705 100644
--- a/conf/example.conf.in
+++ b/conf/example.conf.in
@@ -252,6 +252,21 @@ allocation {
# be placed on different PVs from the pool data.
thin_pool_metadata_require_separate_pvs = 0
+ # Specify chunk size calculation method for thin pool volumes.
+ # Possible options are:
+ # "default" - if thin_pool_chunk_size is defined, use it.
+ # Otherwise, calcucate the chunk size based on
+ # estimation and device hints exposed in sysfs:
+ # the minimum_io_size. The chunk size is always
+ # at least 64KiB.
+ #
+ # "performance" - if thin_pool_chunk_size is defined, use it.
+ # Otherwise, calculate the chunk size for
+ # performance based on device hints exposed in
+ # sysfs: the optimal_io_size. The chunk size is
+ # always at least 512KiB.
+ # thin_pool_chunk_size_calculation = "default"
+
# Specify the minimal chunk size (in KB) for thin pool volumes.
# Use of the larger chunk size may improve perfomance for plain
# thin volumes, however using them for snapshot volumes is less efficient,
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 3549b6d..8ea2c92 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -107,8 +107,10 @@ cfg(allocation_mirror_logs_require_separate_pvs_CFG, "mirror_logs_require_separa
cfg(allocation_thin_pool_metadata_require_separate_pvs_CFG, "thin_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 89), NULL)
cfg(allocation_thin_pool_zero_CFG, "thin_pool_zero", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_ZERO, vsn(2, 2, 99), NULL)
cfg(allocation_thin_pool_discards_CFG, "thin_pool_discards", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_STRING, DEFAULT_THIN_POOL_DISCARDS, vsn(2, 2, 99), NULL)
+cfg(allocation_thin_pool_chunk_size_calculation_CFG, "thin_pool_chunk_size_calculation", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_STRING, DEFAULT_THIN_POOL_CHUNK_SIZE_CALCULATION, vsn(2, 2, 101), NULL)
cfg(allocation_thin_pool_chunk_size_CFG, "thin_pool_chunk_size", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_INT, DEFAULT_THIN_POOL_CHUNK_SIZE, vsn(2, 2, 99), NULL)
+
cfg(log_verbose_CFG, "verbose", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_VERBOSE, vsn(1, 0, 0), NULL)
cfg(log_silent_CFG, "silent", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SILENT, vsn(2, 2, 98), NULL)
cfg(log_syslog_CFG, "syslog", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SYSLOG, vsn(1, 0, 0), NULL)
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index d200331..361d927 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -71,6 +71,7 @@
#define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */
#define DEFAULT_THIN_POOL_MIN_METADATA_SIZE 2048 /* KB */
#define DEFAULT_THIN_POOL_OPTIMAL_SIZE (128 * 1024 * 1024) /* KB */
+#define DEFAULT_THIN_POOL_CHUNK_SIZE_CALCULATION "default"
#define DEFAULT_THIN_POOL_CHUNK_SIZE 64 /* KB */
#define DEFAULT_THIN_POOL_DISCARDS "passdown"
#define DEFAULT_THIN_POOL_ZERO 1
10 years, 6 months
master - Test/clean-up: Indent clean-up and additional RAID resize test
by Jonathan Brassow
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=c37c59e1558135...
Commit: c37c59e155813545c2e674eb370a4609e97aa769
Parent: 5ded7314ae00629da8d21d925c3fa091cce2a939
Author: Jonathan Brassow <jbrassow(a)redhat.com>
AuthorDate: Tue Sep 24 21:32:53 2013 -0500
Committer: Jonathan Brassow <jbrassow(a)redhat.com>
CommitterDate: Tue Sep 24 21:32:53 2013 -0500
Test/clean-up: Indent clean-up and additional RAID resize test
Better indenting and a test for bug 1005434 (parity RAID should
extend in a contiguous fashion).
---
lib/metadata/lv_manip.c | 26 ++++++++++++++++++--------
test/shell/lvresize-raid.sh | 10 +++++++++-
2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index c16288e..d1ec4cd 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -1190,7 +1190,9 @@ static uint32_t _stripes_per_mimage(struct lv_segment *seg)
return 1;
}
-static void _init_alloc_parms(struct alloc_handle *ah, struct alloc_parms *alloc_parms, alloc_policy_t alloc,
+static void _init_alloc_parms(struct alloc_handle *ah,
+ struct alloc_parms *alloc_parms,
+ alloc_policy_t alloc,
struct lv_segment *prev_lvseg, unsigned can_split,
uint32_t allocated, uint32_t extents_still_needed)
{
@@ -1199,23 +1201,29 @@ static void _init_alloc_parms(struct alloc_handle *ah, struct alloc_parms *alloc
alloc_parms->flags = 0;
alloc_parms->extents_still_needed = extents_still_needed;
- /* Only attempt contiguous/cling allocation to previous segment areas if the number of areas matches. */
+ /*
+ * Only attempt contiguous/cling allocation to previous segment
+ * areas if the number of areas matches.
+ */
if (alloc_parms->prev_lvseg &&
((ah->area_count + ah->parity_count) == prev_lvseg->area_count))
alloc_parms->flags |= A_AREA_COUNT_MATCHES;
/* Are there any preceding segments we must follow on from? */
- if (alloc_parms->prev_lvseg && (alloc_parms->flags & A_AREA_COUNT_MATCHES)) {
+ if (alloc_parms->prev_lvseg &&
+ (alloc_parms->flags & A_AREA_COUNT_MATCHES)) {
if (alloc_parms->alloc == ALLOC_CONTIGUOUS)
alloc_parms->flags |= A_CONTIGUOUS_TO_LVSEG;
- else if ((alloc_parms->alloc == ALLOC_CLING) || (alloc_parms->alloc == ALLOC_CLING_BY_TAGS))
+ else if ((alloc_parms->alloc == ALLOC_CLING) ||
+ (alloc_parms->alloc == ALLOC_CLING_BY_TAGS))
alloc_parms->flags |= A_CLING_TO_LVSEG;
} else
/*
- * A cling allocation that follows a successful contiguous allocation
- * must use the same PVs (or else fail).
+ * A cling allocation that follows a successful contiguous
+ * allocation must use the same PVs (or else fail).
*/
- if ((alloc_parms->alloc == ALLOC_CLING) || (alloc_parms->alloc == ALLOC_CLING_BY_TAGS))
+ if ((alloc_parms->alloc == ALLOC_CLING) ||
+ (alloc_parms->alloc == ALLOC_CLING_BY_TAGS))
alloc_parms->flags |= A_CLING_TO_ALLOCED;
if (alloc_parms->alloc == ALLOC_CLING_BY_TAGS)
@@ -1226,7 +1234,9 @@ static void _init_alloc_parms(struct alloc_handle *ah, struct alloc_parms *alloc
* for allocation, prefer to place further extents on the same disks as
* have already been used.
*/
- if (ah->maximise_cling && alloc_parms->alloc == ALLOC_NORMAL && allocated != alloc_parms->extents_still_needed)
+ if (ah->maximise_cling &&
+ (alloc_parms->alloc == ALLOC_NORMAL) &&
+ (allocated != alloc_parms->extents_still_needed))
alloc_parms->flags |= A_CLING_TO_ALLOCED;
if (can_split)
diff --git a/test/shell/lvresize-raid.sh b/test/shell/lvresize-raid.sh
index e61f194..6908635 100644
--- a/test/shell/lvresize-raid.sh
+++ b/test/shell/lvresize-raid.sh
@@ -13,7 +13,7 @@
aux target_at_least dm-raid 1 1 0 || skip
-aux prepare_vg 5 80
+aux prepare_vg 6 80
for deactivate in true false; do
@@ -45,3 +45,11 @@ for deactivate in true false; do
lvremove -ff $vg
done
done
+
+# Bug 1005434
+# Ensure extend is contiguous
+lvcreate --type raid4 -l 2 -i 2 -n $lv1 $vg $dev4 $dev5 $dev6
+lvextend -l +2 --alloc contiguous $vg/$lv1
+not $(lvs -a -o devices $vg | egrep "$dev1|$dev2|$dev3")
+lvremove -ff $vg
+
10 years, 6 months
master - RAID: Fix broken allocation policies for parity RAID types
by Jonathan Brassow
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=5ded7314ae0062...
Commit: 5ded7314ae00629da8d21d925c3fa091cce2a939
Parent: 6553f86818404cd34fa3600dc976ed25fcf6d65a
Author: Jonathan Brassow <jbrassow(a)redhat.com>
AuthorDate: Tue Sep 24 21:32:10 2013 -0500
Committer: Jonathan Brassow <jbrassow(a)redhat.com>
CommitterDate: Tue Sep 24 21:32:10 2013 -0500
RAID: Fix broken allocation policies for parity RAID types
A previous commit (b6bfddcd0a830d0c9312bc3ab906cb3d1b7a6dd9) which
was designed to prevent segfaults during lvextend when trying to
extend striped logical volumes forgot to include calculations for
RAID4/5/6 parity devices. This was causing the 'contiguous' and
'cling_by_tags' allocation policies to fail for RAID 4/5/6.
The solution is to remember that while we can compare
ah->area_count == prev_lvseg->area_count
for non-RAID, we should compare
(ah->area_count + ah->parity_count) == prev_lvseg->area_count
for a general solution.
---
WHATS_NEW | 1 +
lib/metadata/lv_manip.c | 3 ++-
2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 1aebdf4..8a50122 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.103 -
======================================
+ Fix contiguous & cling allocation policies for parity RAID. (2.02.100)
Set use_lvmetad=0 on lvmconf --enable-cluster, reset to default on --disable-cluster.
Don't install separate command symlink in binary directory for 'lvm devtypes'.
Add seg_size_pe field to reports.
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index a145c41..c16288e 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -1200,7 +1200,8 @@ static void _init_alloc_parms(struct alloc_handle *ah, struct alloc_parms *alloc
alloc_parms->extents_still_needed = extents_still_needed;
/* Only attempt contiguous/cling allocation to previous segment areas if the number of areas matches. */
- if (alloc_parms->prev_lvseg && (ah->area_count == prev_lvseg->area_count))
+ if (alloc_parms->prev_lvseg &&
+ ((ah->area_count + ah->parity_count) == prev_lvseg->area_count))
alloc_parms->flags |= A_AREA_COUNT_MATCHES;
/* Are there any preceding segments we must follow on from? */
10 years, 6 months
master - lvmconf: use_lvmetad=0 on --enable-cluster, reset to default on --disable-cluster
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=6553f86818404c...
Commit: 6553f86818404cd34fa3600dc976ed25fcf6d65a
Parent: f050278a35ec0049fa333ce82abfc2bf6a231c62
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Tue Sep 24 14:03:42 2013 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Sep 24 14:03:42 2013 +0200
lvmconf: use_lvmetad=0 on --enable-cluster, reset to default on --disable-cluster
lvmetad is not yet supported in clustered environment so
disable it automatically if using lvmconf --enable-cluster
and reset it to default value if using lvmconf --disable-cluster.
Also, add a few comments in lvm.conf about locking_type vs. use_lvmetad
if setting it for clustered environment.
---
WHATS_NEW | 1 +
conf/example.conf.in | 8 ++++++++
man/lvmconf.8.in | 4 +++-
scripts/lvmconf.sh | 19 ++++++++++++++++++-
4 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index ac2a84e..1aebdf4 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.103 -
======================================
+ Set use_lvmetad=0 on lvmconf --enable-cluster, reset to default on --disable-cluster.
Don't install separate command symlink in binary directory for 'lvm devtypes'.
Add seg_size_pe field to reports.
Support start+length notation with command line PE ranges.
diff --git a/conf/example.conf.in b/conf/example.conf.in
index fb96079..6adcecf 100644
--- a/conf/example.conf.in
+++ b/conf/example.conf.in
@@ -432,6 +432,10 @@ global {
# Type 3 uses built-in clustered locking.
# Type 4 uses read-only locking which forbids any operations that might
# change metadata.
+ # N.B. Don't use lvmetad with locking type 3 as lvmetad is not yet
+ # supported in clustered environment. If use_lvmetad=1 and locking_type=3
+ # is set at the same time, LVM always issues a warning message about this
+ # and then it automatically disables lvmetad use.
locking_type = 1
# Set to 0 to fail when a lock request cannot be satisfied immediately.
@@ -571,6 +575,10 @@ global {
# setting itself.
# To prevent scanning devices completely, even when using lvmetad,
# the devices/global_filter must be used.
+ # N.B. Don't use lvmetad with locking type 3 as lvmetad is not yet
+ # supported in clustered environment. If use_lvmetad=1 and locking_type=3
+ # is set at the same time, LVM always issues a warning message about this
+ # and then it automatically disables lvmetad use.
use_lvmetad = 0
# Full path of the utility called to check that a thin metadata device
diff --git a/man/lvmconf.8.in b/man/lvmconf.8.in
index 0722e22..2c822ee 100644
--- a/man/lvmconf.8.in
+++ b/man/lvmconf.8.in
@@ -22,10 +22,12 @@ an lvm configuration file. See \fBlvm.conf\fP(5).
.SH "OPTIONS"
.TP
.BR \-\-disable-cluster
-Set \fBlocking_type\fR to the default non-clustered type.
+Set \fBlocking_type\fR to the default non-clustered type. Also reset
+lvmetad use to its default.
.TP
.BR \-\-enable-cluster
Set \fBlocking_type\fR to the default clustered type on this system.
+Also disable lvmetad use as it is not yet supported in clustered environment.
.TP
.BR \-\-file " <" \fIconfigfile >
Apply the changes to \fIconfigfile\fP instead of the default
diff --git a/scripts/lvmconf.sh b/scripts/lvmconf.sh
index 5a8e9e8..bfe518e 100644
--- a/scripts/lvmconf.sh
+++ b/scripts/lvmconf.sh
@@ -16,6 +16,8 @@
# Edit an lvm.conf file to adjust various properties
#
+DEFAULT_USE_LVMETAD=0
+
function usage
{
echo "usage: $0 <command>"
@@ -37,10 +39,12 @@ function parse_args
case $1 in
--enable-cluster)
LOCKING_TYPE=3
+ USE_LVMETAD=0
shift
;;
--disable-cluster)
LOCKING_TYPE=1
+ USE_LVMETAD=$DEFAULT_USE_LVMETAD
shift
;;
--lockinglibdir)
@@ -129,6 +133,7 @@ TMPFILE=/etc/lvm/.lvmconf-tmp.tmp
have_type=1
have_dir=1
have_library=1
+have_use_lvmetad=1
have_global=1
grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $CONFIGFILE
@@ -140,8 +145,11 @@ have_dir=$?
grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $CONFIGFILE
have_library=$?
+grep -q '^[[:blank:]]*use_lvmetad[[:blank:]]*=' $CONFIGFILE
+have_use_lvmetad=$?
+
# Those options are in section "global {" so we must have one if any are present.
-if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
+if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" -o "$have_use_lvmetad" = "0" ]
then
# See if we can find it...
@@ -174,6 +182,8 @@ global {
# Enable locking for cluster LVM
locking_type = $LOCKING_TYPE
library_dir = "$LOCKINGLIBDIR"
+ # Disable lvmetad in cluster
+ use_lvmetad = 0
EOF
if [ $? != 0 ]
then
@@ -233,6 +243,13 @@ else
fi
fi
+ if [ "$have_use_lvmetad" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\ns'^[[:blank:]]*use_lvmetad[[:blank:]]*=.*'\ \ \ \ use_lvmetad = $USE_LVMETAD'g"
+ else
+ SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ use_lvmetad = $USE_LVMETAD"
+ fi
+
echo -e $SEDCMD > $SCRIPTFILE
sed <$CONFIGFILE >$TMPFILE -f $SCRIPTFILE
if [ $? != 0 ]
10 years, 6 months
master - tools: don't install separate command symlink for lvm devtypes
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=f050278a35ec00...
Commit: f050278a35ec0049fa333ce82abfc2bf6a231c62
Parent: 11dc6a03c4cdd10bc4065c7d7a8585373455f947
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Tue Sep 24 09:35:20 2013 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Sep 24 09:35:20 2013 +0200
tools: don't install separate command symlink for lvm devtypes
---
WHATS_NEW | 1 +
tools/Makefile.in | 2 +-
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index efd83e6..ac2a84e 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.103 -
======================================
+ Don't install separate command symlink in binary directory for 'lvm devtypes'.
Add seg_size_pe field to reports.
Support start+length notation with command line PE ranges.
Exit cleanly with message when pvmove cannot restart because LV is inactive.
diff --git a/tools/Makefile.in b/tools/Makefile.in
index 96dd8cf..f90cfae 100644
--- a/tools/Makefile.in
+++ b/tools/Makefile.in
@@ -162,7 +162,7 @@ liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION): liblvm2cmd.$(LIB_SUFFIX)
.commands: $(srcdir)/commands.h $(srcdir)/cmdnames.h Makefile
$(CC) -E -P $(srcdir)/cmdnames.h 2> /dev/null | \
- egrep -v '^ *(|#.*|dumpconfig|formats|help|pvdata|segtypes|version) *$$' > .commands
+ egrep -v '^ *(|#.*|devtypes|dumpconfig|formats|help|pvdata|segtypes|version) *$$' > .commands
ifneq ("$(CFLOW_CMD)", "")
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
10 years, 6 months
master - lvs: Add seg_size_pe field.
by Alasdair Kergon
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=11dc6a03c4cdd1...
Commit: 11dc6a03c4cdd10bc4065c7d7a8585373455f947
Parent: 8de47abcd231fecde7c0627ff5085f8efef12583
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Mon Sep 23 21:50:14 2013 +0100
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Mon Sep 23 21:50:14 2013 +0100
lvs: Add seg_size_pe field.
Requested
https://www.redhat.com/archives/linux-lvm/2013-July/msg00112.html
---
WHATS_NEW | 1 +
lib/report/columns.h | 1 +
lib/report/properties.c | 2 ++
lib/report/report.c | 11 +++++++++++
man/lvs.8.in | 1 +
5 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 4ffbe4f..efd83e6 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.103 -
======================================
+ Add seg_size_pe field to reports.
Support start+length notation with command line PE ranges.
Exit cleanly with message when pvmove cannot restart because LV is inactive.
diff --git a/lib/report/columns.h b/lib/report/columns.h
index 9bb99d3..6df9771 100644
--- a/lib/report/columns.h
+++ b/lib/report/columns.h
@@ -125,6 +125,7 @@ FIELD(SEGS, seg, NUM, "TransId", list, 4, transactionid, transaction_id, "For th
FIELD(SEGS, seg, NUM, "Start", list, 5, segstart, seg_start, "Offset within the LV to the start of the segment in current units.", 0)
FIELD(SEGS, seg, NUM, "Start", list, 5, segstartpe, seg_start_pe, "Offset within the LV to the start of the segment in physical extents.", 0)
FIELD(SEGS, seg, NUM, "SSize", list, 5, segsize, seg_size, "Size of segment in current units.", 0)
+FIELD(SEGS, seg, NUM, "SSize", list, 5, segsizepe, seg_size_pe, "Size of segment in physical extents.", 0)
FIELD(SEGS, seg, STR, "Seg Tags", tags, 8, tags, seg_tags, "Tags, if any.", 0)
FIELD(SEGS, seg, STR, "PE Ranges", list, 9, peranges, seg_pe_ranges, "Ranges of Physical Extents of underlying devices in command line format.", 0)
FIELD(SEGS, seg, STR, "Devices", list, 7, devices, devices, "Underlying devices used with starting extent numbers.", 0)
diff --git a/lib/report/properties.c b/lib/report/properties.c
index 62a1a35..56200f0 100644
--- a/lib/report/properties.c
+++ b/lib/report/properties.c
@@ -315,6 +315,8 @@ GET_LVSEG_NUM_PROPERTY_FN(seg_start_pe, lvseg->le)
#define _seg_start_pe_set prop_not_implemented_set
GET_LVSEG_NUM_PROPERTY_FN(seg_size, (SECTOR_SIZE * lvseg_size(lvseg)))
#define _seg_size_set prop_not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(seg_size_pe, lvseg->len)
+#define _seg_size_pe_set prop_not_implemented_set
GET_LVSEG_STR_PROPERTY_FN(seg_tags, lvseg_tags_dup(lvseg))
#define _seg_tags_set prop_not_implemented_set
GET_LVSEG_STR_PROPERTY_FN(seg_pe_ranges,
diff --git a/lib/report/report.c b/lib/report/report.c
index 540181a..46edf56 100644
--- a/lib/report/report.c
+++ b/lib/report/report.c
@@ -560,6 +560,17 @@ static int _segsize_disp(struct dm_report *rh, struct dm_pool *mem,
return _size64_disp(rh, mem, field, &size, private);
}
+static int _segsizepe_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct lv_segment *seg = (const struct lv_segment *) data;
+
+ return dm_report_field_uint32(rh, field, &seg->len);
+}
+
static int _chunksize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
diff --git a/man/lvs.8.in b/man/lvs.8.in
index 32cb788..85f7d33 100644
--- a/man/lvs.8.in
+++ b/man/lvs.8.in
@@ -113,6 +113,7 @@ segtype,
seg_count,
seg_pe_ranges,
seg_size,
+seg_size_pe,
seg_start,
seg_start_pe,
seg_tags,
10 years, 6 months
master - pvmove: Accept PE ranges as start+length.
by Alasdair Kergon
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=7233e584adeadd...
Commit: 7233e584adeadd1186eb8cb26d45d65c1f446725
Parent: bbcc120e5a2c781dc372c4d7329a262247f68f98
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Mon Sep 23 19:50:34 2013 +0100
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Mon Sep 23 19:50:34 2013 +0100
pvmove: Accept PE ranges as start+length.
---
WHATS_NEW | 1 +
man/pvmove.8.in | 7 +++++++
tools/toollib.c | 11 ++++++++++-
3 files changed, 18 insertions(+), 1 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 8c1a343..4ffbe4f 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.103 -
======================================
+ Support start+length notation with command line PE ranges.
Exit cleanly with message when pvmove cannot restart because LV is inactive.
Version 2.02.102 - 23rd September 2013
diff --git a/man/pvmove.8.in b/man/pvmove.8.in
index 79ccf1c..c77dcbc 100644
--- a/man/pvmove.8.in
+++ b/man/pvmove.8.in
@@ -123,6 +123,13 @@ inclusive on /dev/sdb1 - like this:
.sp
.B pvmove /dev/sdb1:1000-1999
.P
+A range can also be specified as start+length, so
+.sp
+.B pvmove /dev/sdb1:1000+1000
+.P
+also refers to 1000 Physical Extents starting from Physical Extent number 1000.
+(Counting starts from 0, so this refers to the 1001st to the 2000th inclusive.)
+.P
To move a range of Physical Extents to a specific location (which must have
sufficent free extents) use the form:
.sp
diff --git a/tools/toollib.c b/tools/toollib.c
index 83789df..099e82b 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1081,7 +1081,7 @@ static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges,
const char *pvname, uint32_t size)
{
char *endptr;
- uint32_t start, end;
+ uint32_t start, end, len;
/* Default to whole PV */
if (!c) {
@@ -1121,7 +1121,16 @@ static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges,
goto error;
c = endptr;
}
+ } else if (*c == '+') { /* Length? */
+ c++;
+ if (isdigit(*c)) {
+ if (!xstrtouint32(c, &endptr, 10, &len))
+ goto error;
+ c = endptr;
+ end = start + (len ? (len - 1) : 0);
+ }
}
+
if (*c && *c != ':')
goto error;
10 years, 6 months