mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-13 05:44:19 +03:00
Compare commits
2 Commits
v2_03_27
...
dev-dct-sc
Author | SHA1 | Date | |
---|---|---|---|
|
31e2c4c1a6 | ||
|
09b0eea6a0 |
115
lib/cache/lvmcache.c
vendored
115
lib/cache/lvmcache.c
vendored
@@ -88,6 +88,9 @@ static int _vgs_locked = 0;
|
||||
static int _found_duplicate_vgnames = 0;
|
||||
static int _outdated_warning = 0;
|
||||
|
||||
static const char *_scan_lock_global_file = DEFAULT_RUN_DIR "/scan_lock_global";
|
||||
static int _scan_lock_global_file_exists = 0;
|
||||
|
||||
int lvmcache_init(struct cmd_context *cmd)
|
||||
{
|
||||
/*
|
||||
@@ -2742,19 +2745,115 @@ bool lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const c
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t _max_metadata_size;
|
||||
/*
|
||||
* max_size_bytes and max_size_percent may come from different areas and
|
||||
* different vgs because of different area sizes.
|
||||
*/
|
||||
static uint64_t _max_metadata_size_bytes;
|
||||
static dm_percent_t _max_metadata_size_percent = DM_PERCENT_INVALID;
|
||||
|
||||
void lvmcache_save_metadata_size(uint64_t val)
|
||||
void lvmcache_save_metadata_size_bytes(uint64_t val)
|
||||
{
|
||||
if (!_max_metadata_size)
|
||||
_max_metadata_size = val;
|
||||
else if (_max_metadata_size < val)
|
||||
_max_metadata_size = val;
|
||||
if (!_max_metadata_size_bytes)
|
||||
_max_metadata_size_bytes = val;
|
||||
else if (_max_metadata_size_bytes < val)
|
||||
_max_metadata_size_bytes = val;
|
||||
}
|
||||
|
||||
uint64_t lvmcache_max_metadata_size(void)
|
||||
uint64_t lvmcache_max_metadata_size_bytes(void)
|
||||
{
|
||||
return _max_metadata_size;
|
||||
return _max_metadata_size_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: enable/disable scan_lock_global with config setting:
|
||||
* y: always use it
|
||||
* n: never use it
|
||||
* auto (default): use based on /run/lvm/scan_lock_global
|
||||
*/
|
||||
void lvmcache_save_metadata_size_percent(uint64_t meta_size, uint64_t mdah_size)
|
||||
{
|
||||
|
||||
dm_percent_t pc = dm_make_percent(meta_size, mdah_size);
|
||||
|
||||
if (pc == DM_PERCENT_INVALID || pc == DM_PERCENT_FAILED ||
|
||||
pc == DM_PERCENT_0 || pc == DM_PERCENT_1)
|
||||
return;
|
||||
|
||||
if (_max_metadata_size_percent == DM_PERCENT_INVALID) {
|
||||
_max_metadata_size_percent = pc;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_max_metadata_size_percent < pc)
|
||||
_max_metadata_size_percent = pc;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: make the percent at which scan_lock_global is used
|
||||
* configurable?
|
||||
*/
|
||||
#define SCAN_LOCK_GLOBAL_METADATA_PERCENT (DM_PERCENT_1 * 25)
|
||||
|
||||
void set_scan_lock_global(struct cmd_context *cmd)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if (_max_metadata_size_percent == DM_PERCENT_INVALID)
|
||||
return;
|
||||
|
||||
if (_max_metadata_size_percent >= SCAN_LOCK_GLOBAL_METADATA_PERCENT) {
|
||||
if (_scan_lock_global_file_exists)
|
||||
return;
|
||||
log_debug("Creating %s.", _scan_lock_global_file);
|
||||
if (!(fp = fopen(_scan_lock_global_file, "w")))
|
||||
return;
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
} else {
|
||||
if (_scan_lock_global_file_exists) {
|
||||
log_debug("Unlinking %s.", _scan_lock_global_file);
|
||||
if (unlink(_scan_lock_global_file))
|
||||
stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int do_scan_lock_global(struct cmd_context *cmd, int *gl_ex)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
if (cmd->nolocking)
|
||||
return 0;
|
||||
|
||||
/* global lock is already held */
|
||||
if (cmd->lockf_global_ex)
|
||||
return 0;
|
||||
|
||||
if (!stat(_scan_lock_global_file, &buf)) {
|
||||
_scan_lock_global_file_exists = 1;
|
||||
|
||||
/*
|
||||
* Tell the caller to use sh or ex. A command that may write
|
||||
* vg metadata should use ex, otherwise sh.
|
||||
*
|
||||
* lockd_vg_default_sh/LOCKD_VG_SH is set for commands that
|
||||
* do not modify vg metadata.
|
||||
*
|
||||
* FIXME: this variable/flag was previously used only for
|
||||
* lvmlockd locking logic, but is now more general, so
|
||||
* it should be renamed.
|
||||
*/
|
||||
if (cmd->lockd_vg_default_sh)
|
||||
*gl_ex = 0;
|
||||
else
|
||||
*gl_ex = 1;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lvmcache_vginfo_has_pvid(struct lvmcache_vginfo *vginfo, char *pvid)
|
||||
|
9
lib/cache/lvmcache.h
vendored
9
lib/cache/lvmcache.h
vendored
@@ -183,8 +183,9 @@ bool lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const c
|
||||
|
||||
int lvmcache_vginfo_has_pvid(struct lvmcache_vginfo *vginfo, char *pvid);
|
||||
|
||||
uint64_t lvmcache_max_metadata_size(void);
|
||||
void lvmcache_save_metadata_size(uint64_t val);
|
||||
uint64_t lvmcache_max_metadata_size_bytes(void);
|
||||
void lvmcache_save_metadata_size_bytes(uint64_t val);
|
||||
void lvmcache_save_metadata_size_percent(uint64_t meta_size, uint64_t mdah_size);
|
||||
|
||||
int dev_in_device_list(struct device *dev, struct dm_list *head);
|
||||
|
||||
@@ -226,4 +227,8 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd);
|
||||
|
||||
unsigned int lvmcache_vg_info_count(void);
|
||||
|
||||
void set_scan_lock_global(struct cmd_context *cmd);
|
||||
int do_scan_lock_global(struct cmd_context *cmd, int *gl_ex);
|
||||
|
||||
|
||||
#endif
|
||||
|
@@ -256,6 +256,7 @@ struct cmd_context {
|
||||
unsigned rand_seed;
|
||||
struct dm_list pending_delete; /* list of LVs for removal */
|
||||
struct dm_pool *pending_delete_mem; /* memory pool for pending deletes */
|
||||
int early_lock_vg_mode;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@@ -1646,7 +1646,9 @@ int read_metadata_location_summary(const struct format_type *fmt,
|
||||
vgsummary->mda_size = rlocn->size;
|
||||
|
||||
/* Keep track of largest metadata size we find. */
|
||||
lvmcache_save_metadata_size(rlocn->size);
|
||||
lvmcache_save_metadata_size_bytes(rlocn->size);
|
||||
/* Keep track of the most full metadata area. */
|
||||
lvmcache_save_metadata_size_percent(rlocn->size, mdah->size);
|
||||
|
||||
lvmcache_lookup_mda(vgsummary);
|
||||
|
||||
|
@@ -1288,12 +1288,14 @@ check:
|
||||
*/
|
||||
|
||||
int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
struct dm_list *devs_in, struct dm_list *devs_out)
|
||||
struct dm_list *devs_in, struct dm_list *devs_out, char **vgname_out)
|
||||
{
|
||||
struct dm_list hints_list;
|
||||
int needs_refresh = 0;
|
||||
char *vgname = NULL;
|
||||
|
||||
*vgname_out = NULL;
|
||||
|
||||
dm_list_init(&hints_list);
|
||||
|
||||
/* Decide below if the caller should create new hints. */
|
||||
@@ -1433,7 +1435,7 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
|
||||
dm_list_splice(hints_out, &hints_list);
|
||||
|
||||
free(vgname);
|
||||
*vgname_out = vgname;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ void clear_hint_file(struct cmd_context *cmd);
|
||||
void invalidate_hints(struct cmd_context *cmd);
|
||||
|
||||
int get_hints(struct cmd_context *cmd, struct dm_list *hints, int *newhints,
|
||||
struct dm_list *devs_in, struct dm_list *devs_out);
|
||||
struct dm_list *devs_in, struct dm_list *devs_out, char **vgname_out);
|
||||
|
||||
int validate_hints(struct cmd_context *cmd, struct dm_list *hints);
|
||||
|
||||
|
@@ -1032,6 +1032,7 @@ int label_scan(struct cmd_context *cmd)
|
||||
struct dev_iter *iter;
|
||||
struct device_list *devl, *devl2;
|
||||
struct device *dev;
|
||||
char *vgname_hint = NULL;
|
||||
uint64_t max_metadata_size_bytes;
|
||||
int device_ids_invalid = 0;
|
||||
int using_hints;
|
||||
@@ -1137,21 +1138,54 @@ int label_scan(struct cmd_context *cmd)
|
||||
* by using hints which tell us which devices are PVs, which
|
||||
* are the only devices we actually need to scan. Without
|
||||
* hints we need to scan all devs to find which are PVs.
|
||||
*
|
||||
* TODO: if the command is using hints and a single vgname
|
||||
*/
|
||||
if (!get_hints(cmd, &hints_list, &create_hints, &all_devs, &scan_devs, &vgname_hint)) {
|
||||
dm_list_splice(&scan_devs, &all_devs);
|
||||
dm_list_init(&hints_list);
|
||||
using_hints = 0;
|
||||
} else
|
||||
using_hints = 1;
|
||||
|
||||
/*
|
||||
* If the command is using hints and a single vgname
|
||||
* arg, we can also take the vg lock here, prior to scanning.
|
||||
* This means we would not need to rescan the PVs in the VG
|
||||
* in vg_read (skip lvmcache_label_rescan_vg) after the
|
||||
* vg lock is usually taken. (Some commands are already
|
||||
* able to avoid rescan in vg_read, but locking early would
|
||||
* apply to more cases.)
|
||||
*
|
||||
* TODO: we don't know exactly which vg lock mode (read or write)
|
||||
* the command will use in vg_read() for the normal lock_vol(),
|
||||
* but we could make a fairly accurate guess of READ/WRITE based
|
||||
* on looking at the command name. If we guess wrong we can
|
||||
* just unlock_vg and lock_vol again with the correct mode in
|
||||
* vg_read().
|
||||
*/
|
||||
if (!get_hints(cmd, &hints_list, &create_hints, &all_devs, &scan_devs)) {
|
||||
dm_list_splice(&scan_devs, &all_devs);
|
||||
dm_list_init(&hints_list);
|
||||
using_hints = 0;
|
||||
} else
|
||||
using_hints = 1;
|
||||
if (vgname_hint) {
|
||||
uint32_t lck_type = LCK_VG_WRITE;
|
||||
|
||||
log_debug("Early lock vg");
|
||||
|
||||
/* FIXME: borrowing this lockd flag which should be
|
||||
quite close to what we want, based on the command name.
|
||||
Need to do proper mode selection here, and then check
|
||||
in case the later lock_vol in vg_read wants different. */
|
||||
if (cmd->lockd_vg_default_sh)
|
||||
lck_type = LCK_VG_READ;
|
||||
|
||||
if (!lock_vol(cmd, vgname_hint, lck_type, NULL)) {
|
||||
log_warn("Could not pre-lock VG %s.", vgname_hint);
|
||||
/* not an error since this is just an optimization */
|
||||
} else {
|
||||
/* Save some state indicating that the vg lock
|
||||
is already held so that the normal lock_vol()
|
||||
will know. */
|
||||
cmd->early_lock_vg_mode = lck_type;
|
||||
}
|
||||
|
||||
free(vgname_hint);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the total number of devices exceeds the soft open file
|
||||
@@ -1187,7 +1221,7 @@ int label_scan(struct cmd_context *cmd)
|
||||
* If the largest metadata is within 1MB of the bcache size, then start
|
||||
* warning.
|
||||
*/
|
||||
max_metadata_size_bytes = lvmcache_max_metadata_size();
|
||||
max_metadata_size_bytes = lvmcache_max_metadata_size_bytes();
|
||||
|
||||
if (max_metadata_size_bytes + (1024 * 1024) > _current_bcache_size_bytes) {
|
||||
/* we want bcache to be 1MB larger than the max metadata seen */
|
||||
@@ -1202,6 +1236,14 @@ int label_scan(struct cmd_context *cmd)
|
||||
(unsigned long long)want_size_kb);
|
||||
}
|
||||
|
||||
/*
|
||||
* If vg metadata is using a large percentage of a metadata area, then
|
||||
* create /run/lvm/scan_lock_global to tell future lvm commands to
|
||||
* begin doing lock_global() prior to scanning to avoid problems due to
|
||||
* metadata wrapping between label_scan and vg_read.
|
||||
*/
|
||||
set_scan_lock_global(cmd);
|
||||
|
||||
dm_list_init(&cmd->hints);
|
||||
|
||||
/*
|
||||
|
@@ -203,6 +203,11 @@ int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags, const str
|
||||
if (is_orphan_vg(vol))
|
||||
return 1;
|
||||
|
||||
if (!is_global && cmd->early_lock_vg_mode && (lck_type != LCK_UNLOCK)) {
|
||||
log_debug("VG was locked early.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!_blocking_supported)
|
||||
flags |= LCK_NONBLOCK;
|
||||
|
||||
@@ -354,10 +359,8 @@ static int _lockf_global(struct cmd_context *cmd, const char *mode, int convert,
|
||||
if (!strcmp(mode, "ex")) {
|
||||
flags |= LCK_WRITE;
|
||||
|
||||
if (cmd->lockf_global_ex) {
|
||||
log_warn("global flock already held ex");
|
||||
if (cmd->lockf_global_ex)
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = lock_vol(cmd, VG_GLOBAL, flags, NULL);
|
||||
if (ret)
|
||||
|
@@ -2140,6 +2140,7 @@ int process_each_vg(struct cmd_context *cmd,
|
||||
struct dm_list vgnameids_to_process; /* vgnameid_list */
|
||||
int enable_all_vgs = (cmd->cname->flags & ALL_VGS_IS_DEFAULT);
|
||||
int process_all_vgs_on_system = 0;
|
||||
int gl_ex = 0;
|
||||
int ret_max = ECMD_PROCESSED;
|
||||
int ret;
|
||||
|
||||
@@ -2173,11 +2174,25 @@ int process_each_vg(struct cmd_context *cmd,
|
||||
process_all_vgs_on_system = 1;
|
||||
|
||||
/*
|
||||
* Needed for a current listing of the global VG namespace.
|
||||
* The global lock will be taken prior to scanning if the
|
||||
* /run/lvm/scan_lock_global file has been created by a prior command,
|
||||
* indicating that vg metadata sizes are large enough to possibly wrap
|
||||
* around the metadata area during label_scan or between label_scan and
|
||||
* vg_read, which can invalidate the scan results (normally unlocked)
|
||||
* and prevent a valid vg_read (which uses metadata locations saved by
|
||||
* label_scan).
|
||||
*/
|
||||
if (process_all_vgs_on_system && !lock_global(cmd, "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
if (do_scan_lock_global(cmd, &gl_ex)) {
|
||||
if (!lock_global(cmd, gl_ex ? "ex" : "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
}
|
||||
} else if (process_all_vgs_on_system) {
|
||||
/* Needed for a current listing of the global VG namespace. */
|
||||
if (!lock_global(cmd, "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3668,6 +3683,7 @@ int process_each_lv(struct cmd_context *cmd,
|
||||
struct dm_list vgnameids_to_process; /* vgnameid_list */
|
||||
int enable_all_vgs = (cmd->cname->flags & ALL_VGS_IS_DEFAULT);
|
||||
int process_all_vgs_on_system = 0;
|
||||
int gl_ex = 0;
|
||||
int ret_max = ECMD_PROCESSED;
|
||||
int ret;
|
||||
|
||||
@@ -3722,12 +3738,17 @@ int process_each_lv(struct cmd_context *cmd,
|
||||
else if (dm_list_empty(&arg_vgnames) && handle->internal_report_for_select)
|
||||
process_all_vgs_on_system = 1;
|
||||
|
||||
/*
|
||||
* Needed for a current listing of the global VG namespace.
|
||||
*/
|
||||
if (process_all_vgs_on_system && !lock_global(cmd, "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
if (do_scan_lock_global(cmd, &gl_ex)) {
|
||||
if (!lock_global(cmd, gl_ex ? "ex" : "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
}
|
||||
} else if (process_all_vgs_on_system) {
|
||||
/* Needed for a current listing of the global VG namespace. */
|
||||
if (!lock_global(cmd, "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4384,6 +4405,7 @@ int process_each_pv(struct cmd_context *cmd,
|
||||
struct device_id_list *dil;
|
||||
int process_all_pvs;
|
||||
int process_all_devices;
|
||||
int gl_ex = 0;
|
||||
int ret_max = ECMD_PROCESSED;
|
||||
int ret;
|
||||
|
||||
@@ -4434,10 +4456,17 @@ int process_each_pv(struct cmd_context *cmd,
|
||||
|
||||
process_all_devices = process_all_pvs && (cmd->cname->flags & ENABLE_ALL_DEVS) && all_is_set;
|
||||
|
||||
/* Needed for a current listing of the global VG namespace. */
|
||||
if (!only_this_vgname && !lock_global(cmd, "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
if (do_scan_lock_global(cmd, &gl_ex)) {
|
||||
if (!lock_global(cmd, gl_ex ? "ex" : "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
}
|
||||
} else if (!only_this_vgname) {
|
||||
/* Needed for a current listing of the global VG namespace. */
|
||||
if (!lock_global(cmd, "sh")) {
|
||||
ret_max = ECMD_FAILED;
|
||||
goto_out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(read_flags & PROCESS_SKIP_SCAN))
|
||||
|
Reference in New Issue
Block a user