diff --git a/WHATS_NEW b/WHATS_NEW index f6ead678f..f8713777b 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,6 @@ Version 2.02.177 - ==================================== + Change text format metadata alignment from 512 to 4096 bytes. When writing metadata, consistently skip mdas marked as failed. Refactor and adjust text format metadata alignment calculation. Fix python3 path in lvmdbusd to use value detected by configure. diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c index 0c5dd1bd8..ea31a2f50 100644 --- a/lib/format_text/format-text.c +++ b/lib/format_text/format-text.c @@ -488,7 +488,7 @@ static uint64_t _next_rlocn_offset(struct raw_locn *rlocn, struct mda_header *md if (!rlocn) /* Find an empty slot */ /* FIXME Assumes only one VG per mdah for now */ - return alignment; + return ALIGN_ABSOLUTE(MDA_HEADER_SIZE, mdac_area_start, alignment); /* First find the end of the old metadata */ old_end = rlocn->offset + rlocn->size; @@ -501,10 +501,21 @@ static uint64_t _next_rlocn_offset(struct raw_locn *rlocn, struct mda_header *md /* Calculate new start position relative to start of buffer rounded up to absolute alignment */ new_start_offset = ALIGN_ABSOLUTE(old_end, mdac_area_start, alignment); - /* If new location is beyond the end of the buffer, wrap around back to start of circular buffer */ - if (new_start_offset >= mdah->size) - new_start_offset -= (mdah->size - MDA_HEADER_SIZE); + /* If new location is beyond the end of the buffer, return to start of circular buffer and realign */ + if (new_start_offset >= mdah->size) { + /* If the start of the buffer is occupied, move past it */ + if (old_wrapped || rlocn->offset == MDA_HEADER_SIZE) + new_start_offset = old_end; + else + new_start_offset = MDA_HEADER_SIZE; + + new_start_offset = ALIGN_ABSOLUTE(new_start_offset, mdac_area_start, alignment); + } + /* + * Note that we don't check here that this location isn't inside the existing metadata. + * If it is, then it means this value of alignment cannot be used. + */ return new_start_offset; } @@ -640,6 +651,15 @@ static int _metadata_fits_into_buffer(struct mda_context *mdac, struct mda_heade uint64_t old_start = 0; /* The start of the existing metadata */ uint64_t new_start = mdac->rlocn.offset; /* The proposed start of the new metadata */ + /* + * If the (aligned) start of the new metadata is already beyond the end + * of the buffer this means it didn't fit with the given alignment. + * (The caller has already tried to wrap it back to the start + * of the buffer but the alignment pushed it back outside.) + */ + if (new_start >= mdah->size) + return_0; + /* Does the total amount of metadata, old and new, fit inside the buffer? */ if (MDA_HEADER_SIZE + (rlocn ? rlocn->size : 0) + mdac->rlocn.size >= mdah->size) return_0; @@ -654,7 +674,7 @@ static int _metadata_fits_into_buffer(struct mda_context *mdac, struct mda_heade old_wrap = old_end - mdah->size; } - new_end = new_wrap ? new_wrap + MDA_HEADER_SIZE : (mdac->rlocn.offset + mdac->rlocn.size); + new_end = new_wrap ? new_wrap + MDA_HEADER_SIZE : new_start + mdac->rlocn.size; /* If both wrap around, there's necessarily overlap */ if (new_wrap && old_wrap) @@ -668,6 +688,10 @@ static int _metadata_fits_into_buffer(struct mda_context *mdac, struct mda_heade if ((new_wrap || old_wrap) && (new_end > old_start)) return_0; + /* If there's no wrap, check there's no overlap */ + if (!new_wrap && !old_wrap && (old_end > new_start) && (old_start < new_end)) + return_0; + return 1; } @@ -681,7 +705,7 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg, struct pv_list *pvl; int r = 0; uint64_t new_wrap; /* Number of bytes of new metadata that wrap around to start of buffer */ - uint64_t alignment = MDA_ORIGINAL_ALIGNMENT; + uint64_t alignment = MDA_ALIGNMENT; int found = 0; int noprecommit = 0; const char *old_vg_name = NULL; @@ -725,16 +749,32 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg, /* Find where the new metadata would be written with our preferred alignment */ mdac->rlocn.offset = _next_rlocn_offset(rlocn, mdah, mdac->area.start, alignment); - /* Does the new metadata wrap around? */ + /* If metadata extends beyond the buffer, return to the start instead of wrapping it */ if (mdac->rlocn.offset + mdac->rlocn.size > mdah->size) - new_wrap = (mdac->rlocn.offset + mdac->rlocn.size) - mdah->size; - else - new_wrap = 0; + mdac->rlocn.offset = ALIGN_ABSOLUTE(MDA_HEADER_SIZE, mdac->area.start, alignment); - if (!_metadata_fits_into_buffer(mdac, mdah, rlocn, new_wrap)) { - log_error("VG %s metadata on %s (" FMTu64 " bytes) too large for circular buffer (" FMTu64 " bytes with " FMTu64 " used)", - vg->name, dev_name(mdac->area.dev), mdac->rlocn.size, mdah->size - MDA_HEADER_SIZE, rlocn ? rlocn->size : 0); - goto out; + /* + * If the metadata doesn't fit into the buffer correctly with these + * settings, fall back to the 512-byte alignment used by the original + * LVM2 code and allow the metadata to be split into two parts, + * wrapping around from the end of the circular buffer back to the + * beginning. + */ + if (!_metadata_fits_into_buffer(mdac, mdah, rlocn, 0)) { + alignment = MDA_ORIGINAL_ALIGNMENT; + mdac->rlocn.offset = _next_rlocn_offset(rlocn, mdah, mdac->area.start, alignment); + + /* Does the new metadata wrap around? */ + if (mdac->rlocn.offset + mdac->rlocn.size > mdah->size) + new_wrap = (mdac->rlocn.offset + mdac->rlocn.size) - mdah->size; + else + new_wrap = 0; + + if (!_metadata_fits_into_buffer(mdac, mdah, rlocn, new_wrap)) { + log_error("VG %s metadata on %s (" FMTu64 " bytes) too large for circular buffer (" FMTu64 " bytes with " FMTu64 " used)", + vg->name, dev_name(mdac->area.dev), mdac->rlocn.size, mdah->size - MDA_HEADER_SIZE, rlocn ? rlocn->size : 0); + goto out; + } } log_debug_metadata("Writing %s metadata to %s at " FMTu64 " len " FMTu64 " of " FMTu64 " aligned to " FMTu64, @@ -1323,7 +1363,7 @@ int vgname_from_mda(const struct format_type *fmt, (char *)&vgsummary->vgid); if (mda_free_sectors) { - current_usage = ALIGN_ABSOLUTE(rlocn->size, dev_area->start + rlocn->offset, MDA_ORIGINAL_ALIGNMENT); + current_usage = ALIGN_ABSOLUTE(rlocn->size, dev_area->start + rlocn->offset, MDA_ALIGNMENT); buffer_size = mdah->size - MDA_HEADER_SIZE; diff --git a/lib/format_text/layout.h b/lib/format_text/layout.h index 1746b9ccd..1eed664da 100644 --- a/lib/format_text/layout.h +++ b/lib/format_text/layout.h @@ -103,6 +103,7 @@ struct mda_context { #define LVM2_LABEL "LVM2 001" #define MDA_SIZE_MIN (8 * (unsigned) lvm_getpagesize()) #define MDA_ORIGINAL_ALIGNMENT 512 /* Original alignment used for start of VG metadata content */ +#define MDA_ALIGNMENT 4096 /* Default alignment in bytes since 2.02.177 for start of VG metadata content. */ int vgname_from_mda(const struct format_type *fmt, struct mda_header *mdah, int primary_mda, struct device_area *dev_area, struct lvmcache_vgsummary *vgsummary,