From 2908ab3eed4d7feb041c99a4e2fca98a5bcf6922 Mon Sep 17 00:00:00 2001 From: Zdenek Kabelac Date: Tue, 13 Jan 2015 15:23:03 +0100 Subject: [PATCH] thin: errrorwhenfull support Support error_if_no_space feature for thin pools. Report more info about thinpool status: (out_of_data (D), metadata_read_only (M), failed (F) also as health attribute.) --- WHATS_NEW | 1 + WHATS_NEW_DM | 2 ++ conf/example.conf.in | 5 +++++ lib/config/config_settings.h | 1 + lib/config/defaults.h | 1 + lib/format_text/flags.c | 1 + lib/metadata/lv.c | 16 +++++++++++++++ lib/metadata/lv.h | 1 + lib/metadata/lv_manip.c | 2 ++ lib/metadata/merge.c | 7 +++++++ lib/metadata/metadata-exported.h | 4 +++- lib/metadata/segtype.h | 3 +++ lib/report/columns.h | 1 + lib/report/properties.c | 2 ++ lib/report/report.c | 21 ++++++++++++++++++++ lib/report/values.h | 1 + lib/thin/thin.c | 11 ++++++++++- libdm/libdevmapper.h | 12 +++++++++-- libdm/libdm-deptree.c | 34 +++++++++++++++++++++++++++----- tools/args.h | 1 + tools/commands.h | 3 ++- tools/lvcreate.c | 15 ++++++++++++++ 22 files changed, 135 insertions(+), 10 deletions(-) diff --git a/WHATS_NEW b/WHATS_NEW index bbb45c43b..3881d5d47 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,6 @@ Version 2.02.115 - ===================================== + Add support for lvcreate --errorwhenfull y|n for thin pools. Fix lvconvert --repair to honour resilience requirement for segmented RAID LV. Filter out partitioned device-mapper devices as unsuitable for use as PVs. Also notify lvmetad about filtered device if using pvscan --cache DevicePath. diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM index 02b80454f..98f98b67c 100644 --- a/WHATS_NEW_DM +++ b/WHATS_NEW_DM @@ -1,5 +1,7 @@ Version 1.02.93 - ==================================== + Report more info from thin pool status (out of data, metadata-ro, fail). + Support error_if_no_space for thin pool target. Fix segfault while using selection with regex and unbuffered reporting. Add dm_report_compact_fields to remove empty fields from report output. Remove unimplemented dm_report_set_output_selection from libdevmapper.h. diff --git a/conf/example.conf.in b/conf/example.conf.in index 70d3e6b24..21b963b41 100644 --- a/conf/example.conf.in +++ b/conf/example.conf.in @@ -777,6 +777,7 @@ global { # external_origin # metadata_resize # external_origin_extend + # error_if_no_space # # thin_disabled_features = [ "discards", "block_size" ] @@ -937,6 +938,10 @@ activation { # enables or disables this automatic setting of the flag while LVs are created. # auto_set_activation_skip = 1 + # Control error behavior when provisioned device becomes full. + # Set to 1 to instant error when there is missing free space in device. + # error_when_full = 0 + # For RAID or 'mirror' segment types, 'raid_region_size' is the # size (in KiB) of each: # - synchronization operation when initializing diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h index 6de948e86..8978b5b7c 100644 --- a/lib/config/config_settings.h +++ b/lib/config/config_settings.h @@ -205,6 +205,7 @@ cfg_array(activation_auto_activation_volume_list_CFG, "auto_activation_volume_li cfg_array(activation_read_only_volume_list_CFG, "read_only_volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 89), NULL) cfg(activation_mirror_region_size_CFG, "mirror_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(1, 0, 0), NULL) cfg(activation_raid_region_size_CFG, "raid_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(2, 2, 99), NULL) +cfg(activation_error_when_full_CFG, "error_when_full", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ERROR_WHEN_FULL, vsn(2, 2, 115), NULL) cfg(activation_readahead_CFG, "readahead", activation_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_READ_AHEAD, vsn(1, 0, 23), NULL) cfg(activation_raid_fault_policy_CFG, "raid_fault_policy", activation_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_RAID_FAULT_POLICY, vsn(2, 2, 89), NULL) cfg(activation_mirror_device_fault_policy_CFG, "mirror_device_fault_policy", activation_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_MIRROR_DEVICE_FAULT_POLICY, vsn(1, 2, 10), NULL) diff --git a/lib/config/defaults.h b/lib/config/defaults.h index e15926c49..1431de205 100644 --- a/lib/config/defaults.h +++ b/lib/config/defaults.h @@ -44,6 +44,7 @@ #define DEFAULT_PV_MIN_SIZE_KB 2048 #define DEFAULT_LOCKING_LIB "liblvm2clusterlock.so" +#define DEFAULT_ERROR_WHEN_FULL 0 #define DEFAULT_FALLBACK_TO_LOCAL_LOCKING 1 #define DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING 1 #define DEFAULT_WAIT_FOR_LOCKS 1 diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c index e3a00a6f9..cf012710a 100644 --- a/lib/format_text/flags.c +++ b/lib/format_text/flags.c @@ -61,6 +61,7 @@ static const struct flag _lv_flags[] = { {LV_REBUILD, "REBUILD", STATUS_FLAG}, {LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG}, {LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG}, + {LV_ERROR_WHEN_FULL, "ERROR_WHEN_FULL", COMPATIBLE_FLAG}, {LV_NOSCAN, NULL, 0}, {LV_TEMPORARY, NULL, 0}, {POOL_METADATA_SPARE, NULL, 0}, diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c index fb1cd7823..868253505 100644 --- a/lib/metadata/lv.c +++ b/lib/metadata/lv.c @@ -210,6 +210,11 @@ uint64_t lvseg_size(const struct lv_segment *seg) return (uint64_t) seg->len * seg->lv->vg->extent_size; } +uint32_t lv_error_when_full(const struct logical_volume *lv) +{ + return (lv_is_thin_pool(lv) && (lv->status & LV_ERROR_WHEN_FULL)) ? 1 : 0; +} + uint32_t lv_kernel_read_ahead(const struct logical_volume *lv) { struct lvinfo info; @@ -644,6 +649,7 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv) dm_percent_t snap_percent; struct lvinfo info; struct lv_segment *seg; + struct lv_seg_status seg_status; char *repstr; if (!(repstr = dm_pool_zalloc(mem, 11))) { @@ -797,6 +803,16 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv) repstr[8] = 'm'; /* RAID has 'm'ismatches */ } else if (lv->status & LV_WRITEMOSTLY) repstr[8] = 'w'; /* sub-LV has 'w'ritemostly */ + } else if (lv_is_thin_pool(lv)) { + seg_status.mem = lv->vg->cmd->mem; + if (!lv_status(lv->vg->cmd, first_seg(lv), &seg_status)) + repstr[8] = 'X'; /* Unknown */ + else if (((struct dm_status_thin_pool *)seg_status.status)->fail) + repstr[8] = 'F'; + else if (((struct dm_status_thin_pool *)seg_status.status)->out_of_data_space) + repstr[8] = 'D'; + else if (((struct dm_status_thin_pool *)seg_status.status)->read_only) + repstr[8] = 'M'; } if (lv->status & LV_ACTIVATION_SKIP) diff --git a/lib/metadata/lv.h b/lib/metadata/lv.h index 8064ba22f..6c6df737f 100644 --- a/lib/metadata/lv.h +++ b/lib/metadata/lv.h @@ -88,6 +88,7 @@ char *lvseg_monitor_dup(struct dm_pool *mem, const struct lv_segment *seg); char *lvseg_tags_dup(const struct lv_segment *seg); char *lvseg_devices(struct dm_pool *mem, const struct lv_segment *seg); char *lvseg_seg_pe_ranges(struct dm_pool *mem, const struct lv_segment *seg); +uint32_t lv_error_when_full(const struct logical_volume *lv); char *lv_time_dup(struct dm_pool *mem, const struct logical_volume *lv); char *lv_host_dup(struct dm_pool *mem, const struct logical_volume *lv); int lv_set_creation(struct logical_volume *lv, diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index 457cf9ea7..8a3b77905 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -7010,6 +7010,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, stack; goto revert_new_lv; } + if (lp->error_when_full) + lv->status |= LV_ERROR_WHEN_FULL; } else if (pool_lv && seg_is_thin_volume(lp)) { seg = first_seg(lv); pool_seg = first_seg(pool_lv); diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c index 5e091234f..6d661f8f2 100644 --- a/lib/metadata/merge.c +++ b/lib/metadata/merge.c @@ -147,6 +147,13 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg) inc_error_count; } + if ((lv->status & LV_ERROR_WHEN_FULL) && + !seg_can_error_when_full(seg)) { + log_error("LV %s: segment %u (%s) does not support flag " + "ERROR_WHEN_FULL.", lv->name, seg_count, seg->segtype->name); + inc_error_count; + } + if (complete_vg && seg->log_lv && !seg_is_mirrored(seg) && !(seg->status & RAID_IMAGE)) { log_error("LV %s: segment %u log LV %s is not a " diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 14a5ef518..a48aef2a0 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -118,8 +118,9 @@ #define CACHE UINT64_C(0x0001000000000000) /* LV - Internal use only */ #define LV_PENDING_DELETE UINT64_C(0x0004000000000000) /* LV - Internal use only */ +#define LV_ERROR_WHEN_FULL UINT64_C(0x0008000000000000) /* LV - error when full */ -/* Next unused flag: UINT64_C(0x0008000000000000) */ +/* Next unused flag: UINT64_C(0x0010000000000000) */ /* Format features flags */ #define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */ @@ -871,6 +872,7 @@ struct lvcreate_params { struct dm_list *pvh; /* all */ uint64_t permission; /* all */ + unsigned error_when_full; /* when segment supports it */ uint32_t read_ahead; /* all */ int approx_alloc; /* all */ alloc_policy_t alloc; /* all */ diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h index ecb598d9b..4b2df7879 100644 --- a/lib/metadata/segtype.h +++ b/lib/metadata/segtype.h @@ -45,6 +45,7 @@ struct dev_manager; #define SEG_CACHE_POOL 0x00004000U #define SEG_MIRROR 0x00008000U #define SEG_ONLY_EXCLUSIVE 0x00010000U /* In cluster only exlusive activation */ +#define SEG_CAN_ERROR_WHEN_FULL 0x00020000U #define SEG_UNKNOWN 0x80000000U #define segtype_is_cache(segtype) ((segtype)->flags & SEG_CACHE ? 1 : 0) @@ -80,6 +81,7 @@ struct dev_manager; #define seg_cannot_be_zeroed(seg) ((seg)->segtype->flags & SEG_CANNOT_BE_ZEROED ? 1 : 0) #define seg_monitored(seg) ((seg)->segtype->flags & SEG_MONITORED ? 1 : 0) #define seg_only_exclusive(seg) ((seg)->segtype->flags & SEG_ONLY_EXCLUSIVE ? 1 : 0) +#define seg_can_error_when_full(seg) ((seg)->segtype->flags & SEG_CAN_ERROR_WHEN_FULL ? 1 : 0) struct segment_type { struct dm_list list; /* Internal */ @@ -179,6 +181,7 @@ int init_replicator_segtype(struct cmd_context *cmd, struct segtype_library *seg #define THIN_FEATURE_DISCARDS_NON_POWER_2 (1U << 4) #define THIN_FEATURE_METADATA_RESIZE (1U << 5) #define THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND (1U << 6) +#define THIN_FEATURE_ERROR_IF_NO_SPACE (1U << 7) #ifdef THIN_INTERNAL int init_thin_segtypes(struct cmd_context *cmd, struct segtype_library *seglib); diff --git a/lib/report/columns.h b/lib/report/columns.h index 1dd2ae0dc..9f64ac8bc 100644 --- a/lib/report/columns.h +++ b/lib/report/columns.h @@ -52,6 +52,7 @@ FIELD(LVS, lv, BIN, "MergeFailed", lvid, 15, lvmergefailed, lv_merge_failed, "Se FIELD(LVS, lv, BIN, "SnapInvalid", lvid, 15, lvsnapshotinvalid, lv_snapshot_invalid, "Set if snapshot LV is invalid.", 0) FIELD(LVS, lv, STR, "Health", lvid, 15, lvhealthstatus, lv_health_status, "LV health status.", 0) FIELD(LVS, lv, BIN, "SkipAct", lvid, 15, lvskipactivation, lv_skip_activation, "Set if LV is skipped on activation.", 0) +FIELD(LVS, lv, BIN, "WhenFull", lvid, 15, lverrorwhenfull, lv_error_when_full, "For thin pools, behavior when full.", 0) FIELD(LVS, lv, STR, "Active", lvid, 6, lvactive, lv_active, "Active state of the LV.", 0) FIELD(LVS, lv, BIN, "ActLocal", lvid, 10, lvactivelocally, lv_active_locally, "Set if the LV is active locally.", 0) FIELD(LVS, lv, BIN, "ActRemote", lvid, 10, lvactiveremotely, lv_active_remotely, "Set if the LV is active remotely.", 0) diff --git a/lib/report/properties.c b/lib/report/properties.c index 4796000d4..cece62c41 100644 --- a/lib/report/properties.c +++ b/lib/report/properties.c @@ -281,6 +281,8 @@ GET_LV_STR_PROPERTY_FN(lv_attr, lv_attr_dup(lv->vg->vgmem, lv)) GET_LV_NUM_PROPERTY_FN(lv_major, lv->major) #define _lv_major_set prop_not_implemented_set GET_LV_NUM_PROPERTY_FN(lv_minor, lv->minor) +#define _lv_error_when_full_set prop_not_implemented_set +GET_LV_NUM_PROPERTY_FN(lv_error_when_full, lv_error_when_full(lv)) #define _lv_minor_set prop_not_implemented_set GET_LV_NUM_PROPERTY_FN(lv_read_ahead, lv->read_ahead * SECTOR_SIZE) #define _lv_read_ahead_set prop_not_implemented_set diff --git a/lib/report/report.c b/lib/report/report.c index be80e771b..1b1ccca2f 100644 --- a/lib/report/report.c +++ b/lib/report/report.c @@ -726,6 +726,16 @@ static int _int32_disp(struct dm_report *rh, struct dm_pool *mem __attribute__(( return dm_report_field_int32(rh, field, data); } +static int _lverrorwhenfull_disp(struct dm_report *rh, struct dm_pool *mem, + struct dm_report_field *field, + const void *data, void *private __attribute__((unused))) +{ + const struct logical_volume *lv = (const struct logical_volume *) data; + + return _binary_disp(rh, mem, field, lv_error_when_full(lv), + GET_FIRST_RESERVED_NAME(lv_error_when_full_y), private); +} + static int _lvreadahead_disp(struct dm_report *rh, struct dm_pool *mem, struct dm_report_field *field, const void *data, void *private __attribute__((unused))) @@ -1761,6 +1771,7 @@ static int _lvhealthstatus_disp(struct dm_report *rh, struct dm_pool *mem, const void *data, void *private) { const struct logical_volume *lv = (const struct logical_volume *) data; + struct lv_seg_status seg_status; const char *health = ""; uint64_t n; @@ -1776,6 +1787,16 @@ static int _lvhealthstatus_disp(struct dm_report *rh, struct dm_pool *mem, health = "mismatches exist"; } else if (lv->status & LV_WRITEMOSTLY) health = "writemostly"; + } else if (lv_is_thin_pool(lv)) { + seg_status.mem = lv->vg->cmd->mem; + if (!lv_status(lv->vg->cmd, first_seg(lv), &seg_status)) + health = "unknown"; + else if (((struct dm_status_thin_pool *)seg_status.status)->fail) + health = "failed"; + else if (((struct dm_status_thin_pool *)seg_status.status)->out_of_data_space) + health = "out_of_data"; + else if (((struct dm_status_thin_pool *)seg_status.status)->read_only) + health = "metadata_read_only"; } return _string_disp(rh, mem, field, &health, private); diff --git a/lib/report/values.h b/lib/report/values.h index 4ea92bdd8..4323c17d4 100644 --- a/lib/report/values.h +++ b/lib/report/values.h @@ -68,6 +68,7 @@ FIELD_RESERVED_BINARY_VALUE(lv_image_synced, lv_image_synced, "", "image synced" FIELD_RESERVED_BINARY_VALUE(lv_merging, lv_merging, "", "merging") FIELD_RESERVED_BINARY_VALUE(lv_converting, lv_converting, "", "converting") FIELD_RESERVED_BINARY_VALUE(lv_allocation_locked, lv_allocation_locked, "", "allocation locked", "locked") +FIELD_RESERVED_BINARY_VALUE(lv_error_when_full, lv_error_when_full, "", "error when full", "error if no space", "error") FIELD_RESERVED_BINARY_VALUE(lv_fixed_minor, lv_fixed_minor, "", "fixed minor", "fixed") FIELD_RESERVED_BINARY_VALUE(lv_active_locally, lv_active_locally, "", "active locally", "active", "locally") FIELD_RESERVED_BINARY_VALUE(lv_active_remotely, lv_active_remotely, "", "active remotely", "active", "remotely") diff --git a/lib/thin/thin.c b/lib/thin/thin.c index f27f24007..377d6adca 100644 --- a/lib/thin/thin.c +++ b/lib/thin/thin.c @@ -259,6 +259,7 @@ static int _thin_pool_add_target_line(struct dev_manager *dm, uint32_t *pvmove_mirror_count __attribute__((unused))) { static int _no_discards = 0; + static int _no_error_if_no_space = 0; char *metadata_dlid, *pool_dlid; const struct lv_thin_message *lmsg; const struct logical_volume *origin; @@ -314,6 +315,12 @@ static int _thin_pool_add_target_line(struct dev_manager *dm, log_warn_suppress(_no_discards++, "WARNING: Thin pool target does " "not support discards (needs kernel >= 3.4)."); + if (attr & THIN_FEATURE_ERROR_IF_NO_SPACE) + dm_tree_node_set_thin_pool_error_if_no_space(node, (seg->lv->status & LV_ERROR_WHEN_FULL) ? 1 : 0); + else if (seg->lv->status & LV_ERROR_WHEN_FULL) + log_warn_suppress(_no_error_if_no_space++, "WARNING: Thin pool target does " + "not support error if no space (needs version >= 1.10)."); + /* * Add messages only for activation tree. * Otherwise avoid checking for existence of suspended origin. @@ -639,6 +646,7 @@ static int _thin_target_present(struct cmd_context *cmd, { 1, 5, THIN_FEATURE_DISCARDS_NON_POWER_2, "discards_non_power_2" }, { 1, 10, THIN_FEATURE_METADATA_RESIZE, "metadata_resize" }, { 9, 11, THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND, "external_origin_extend" }, + { 1, 10, THIN_FEATURE_ERROR_IF_NO_SPACE, "error_if_no_space" }, }; static const char _lvmconf[] = "global/thin_disabled_features"; @@ -753,7 +761,8 @@ int init_multiple_segtypes(struct cmd_context *cmd, struct segtype_library *segl const char name[16]; uint32_t flags; } reg_segtypes[] = { - { &_thin_pool_ops, "thin-pool", SEG_THIN_POOL | SEG_CANNOT_BE_ZEROED | SEG_ONLY_EXCLUSIVE }, + { &_thin_pool_ops, "thin-pool", SEG_THIN_POOL | SEG_CANNOT_BE_ZEROED | + SEG_ONLY_EXCLUSIVE | SEG_CAN_ERROR_WHEN_FULL }, /* FIXME Maybe use SEG_THIN_VOLUME instead of SEG_VIRTUAL */ { &_thin_ops, "thin", SEG_THIN_VOLUME | SEG_VIRTUAL | SEG_ONLY_EXCLUSIVE } }; diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h index 48adf2241..b164b5162 100644 --- a/libdm/libdevmapper.h +++ b/libdm/libdevmapper.h @@ -366,8 +366,12 @@ struct dm_status_thin_pool { uint64_t used_data_blocks; uint64_t total_data_blocks; uint64_t held_metadata_root; - uint32_t read_only; + uint32_t read_only; /* metadata may not be changed */ dm_thin_discards_t discards; + uint32_t fail : 1; /* all I/O fails */ + uint32_t error_if_no_space : 1; /* otherwise queue_if_no_space */ + uint32_t out_of_data_space : 1; /* metadata may be changed, but data may not be allocated */ + uint32_t reserved : 29; }; int dm_get_status_thin_pool(struct dm_pool *mem, const char *params, @@ -886,7 +890,11 @@ int dm_tree_node_add_thin_pool_message(struct dm_tree_node *node, int dm_tree_node_set_thin_pool_discard(struct dm_tree_node *node, unsigned ignore, unsigned no_passdown); - +/* + * Set error if no space, instead of queueing for thin pool. + */ +int dm_tree_node_set_thin_pool_error_if_no_space(struct dm_tree_node *node, + unsigned error_if_no_space); /* * FIXME: Defines bellow are based on kernel's dm-thin.c defines * MAX_DEV_ID ((1 << 24) - 1) diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c index c3a5df643..cd63061cd 100644 --- a/libdm/libdm-deptree.c +++ b/libdm/libdm-deptree.c @@ -204,6 +204,7 @@ struct load_segment { unsigned skip_block_zeroing; /* Thin_pool */ unsigned ignore_discard; /* Thin_pool target vsn 1.1 */ unsigned no_discard_passdown; /* Thin_pool target vsn 1.1 */ + unsigned error_if_no_space; /* Thin pool target vsn 1.10 */ uint32_t device_id; /* Thin */ }; @@ -2417,9 +2418,10 @@ static int _thin_pool_emit_segment_line(struct dm_task *dmt, { int pos = 0; char pool[DM_FORMAT_DEV_BUFSIZE], metadata[DM_FORMAT_DEV_BUFSIZE]; - int features = (seg->skip_block_zeroing ? 1 : 0) + - (seg->ignore_discard ? 1 : 0) + - (seg->no_discard_passdown ? 1 : 0); + int features = (seg->error_if_no_space ? 1 : 0) + + (seg->ignore_discard ? 1 : 0) + + (seg->no_discard_passdown ? 1 : 0) + + (seg->skip_block_zeroing ? 1 : 0); if (!_build_dev_string(metadata, sizeof(metadata), seg->metadata)) return_0; @@ -2427,8 +2429,9 @@ static int _thin_pool_emit_segment_line(struct dm_task *dmt, if (!_build_dev_string(pool, sizeof(pool), seg->pool)) return_0; - EMIT_PARAMS(pos, "%s %s %d %" PRIu64 " %d%s%s%s", metadata, pool, + EMIT_PARAMS(pos, "%s %s %d %" PRIu64 " %d%s%s%s%s", metadata, pool, seg->data_block_size, seg->low_water_mark, features, + seg->error_if_no_space ? " error_if_no_space" : "", seg->skip_block_zeroing ? " skip_block_zeroing" : "", seg->ignore_discard ? " ignore_discard" : "", seg->no_discard_passdown ? " no_discard_passdown" : "" @@ -3848,6 +3851,19 @@ int dm_tree_node_set_thin_pool_discard(struct dm_tree_node *node, return 1; } +int dm_tree_node_set_thin_pool_error_if_no_space(struct dm_tree_node *node, + unsigned error_if_no_space) +{ + struct load_segment *seg; + + if (!(seg = _get_single_load_segment(node, SEG_THIN_POOL))) + return_0; + + seg->error_if_no_space = error_if_no_space; + + return 1; +} + int dm_tree_node_add_thin_target(struct dm_tree_node *node, uint64_t size, const char *pool_uuid, @@ -3936,7 +3952,15 @@ int dm_get_status_thin_pool(struct dm_pool *mem, const char *params, else /* default discard_passdown */ s->discards = DM_THIN_DISCARDS_PASSDOWN; - s->read_only = (strstr(params + pos, "ro ")) ? 1 : 0; + if (strstr(params + pos, "ro ")) + s->read_only = 1; + else if (strstr(params + pos, "fail")) + s->fail = 1; + else if (strstr(params + pos, "out_of_data_space")) + s->out_of_data_space = 1; + + if (strstr(params + pos, "error_if_no_space")) + s->error_if_no_space = 1; *status = s; diff --git a/tools/args.h b/tools/args.h index 1abe0ebdc..80e9155fe 100644 --- a/tools/args.h +++ b/tools/args.h @@ -39,6 +39,7 @@ arg(deltag_ARG, '\0', "deltag", tag_arg, ARG_GROUPABLE) arg(detachprofile_ARG, '\0', "detachprofile", NULL, 0) arg(discards_ARG, '\0', "discards", discards_arg, 0) arg(driverloaded_ARG, '\0', "driverloaded", yes_no_arg, 0) +arg(errorwhenfull_ARG, '\0', "errorwhenfull", yes_no_arg, 0) arg(force_long_ARG, '\0', "force", NULL, ARG_COUNTABLE) arg(ignoreadvanced_ARG, '\0', "ignoreadvanced", NULL, 0) arg(ignorelockingfailure_ARG, '\0', "ignorelockingfailure", NULL, 0) diff --git a/tools/commands.h b/tools/commands.h index 8b6b60716..6eb8cc5ad 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -267,6 +267,7 @@ xx(lvcreate, "\t[--commandprofile ProfileName]\n" "\t[-d|--debug]\n" "\t[-h|-?|--help]\n" + "\t[--errorwhenfull {y|n}]\n" "\t[--ignoremonitoring]\n" "\t[--monitor {y|n}]\n" "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n" @@ -339,7 +340,7 @@ xx(lvcreate, addtag_ARG, alloc_ARG, autobackup_ARG, activate_ARG, available_ARG, cache_ARG, cachemode_ARG, cachepool_ARG, cachepolicy_ARG, cachesettings_ARG, - chunksize_ARG, contiguous_ARG, corelog_ARG, discards_ARG, + chunksize_ARG, contiguous_ARG, corelog_ARG, discards_ARG, errorwhenfull_ARG, extents_ARG, ignoreactivationskip_ARG, ignoremonitoring_ARG, major_ARG, metadataprofile_ARG, minor_ARG, mirrorlog_ARG, mirrors_ARG, monitor_ARG, minrecoveryrate_ARG, maxrecoveryrate_ARG, name_ARG, nosync_ARG, diff --git a/tools/lvcreate.c b/tools/lvcreate.c index 7236ffca5..fd5ac3954 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -582,6 +582,14 @@ static int _read_activation_params(struct cmd_context *cmd, lp->activate = (activation_change_t) arg_uint_value(cmd, activate_ARG, CHANGE_AY); + /* Error when full */ + if (arg_is_set(cmd, errorwhenfull_ARG)) { + lp->error_when_full = arg_uint_value(cmd, errorwhenfull_ARG, 0); + } else + lp->error_when_full = + seg_can_error_when_full(lp) && + find_config_tree_bool(cmd, activation_error_when_full_CFG, NULL); + /* Read ahead */ lp->read_ahead = arg_uint_value(cmd, readahead_ARG, cmd->default_settings.read_ahead); @@ -856,6 +864,7 @@ static int _lvcreate_params(struct cmd_context *cmd, SIZE_ARGS, THIN_POOL_ARGS, chunksize_ARG, + errorwhenfull_ARG, snapshot_ARG, thin_ARG, virtualsize_ARG, @@ -875,6 +884,7 @@ static int _lvcreate_params(struct cmd_context *cmd, SIZE_ARGS, chunksize_ARG, discards_ARG, + errorwhenfull_ARG, zero_ARG, -1)) return_0; @@ -941,6 +951,11 @@ static int _lvcreate_params(struct cmd_context *cmd, -1)) return_0; + if (!seg_can_error_when_full(lp) && arg_is_set(cmd, errorwhenfull_ARG)) { + log_error("Segment type %s does not support --errorwhenfull.", lp->segtype->name); + return 0; + } + /* Basic segment type validation finished here */ if (activation() && lp->segtype->ops->target_present) {