1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-12-30 08:32:45 +03:00

Compare commits

..

32 Commits

Author SHA1 Message Date
David Teigland
97b933cfd9 dm-integrity with raid1 support
Create a raid1 LV where the raid images use dm-integrity.
Only external metadata is currently enabled.

lvcreate --type raid1 --mirrors Num --integrity y ...
2019-12-05 16:56:28 -06:00
David Teigland
1b3f34b39a dm-integrity support
Create a linear LV with a dm-integrity layer added
above it.  The dm-integrity layer stores checksums
of the data written to the LV, and returns an error
if data read from the LV does not match the previously
saved checksum.

lvcreate --type integrity --integrity String [options]

The --integrity String specifies if the dm-integrity
metadata (checksums) should be interleaved with data
blocks, or written to a separate external LV:

  --integrity external (default)
  Store integrity metadata on a separate LV.
  Allows removing integrity from the LV later.

  --integrity internal
  Store integrity metadata interleaved with data
  on the same LV.  Around 1% of the LV size will
  be used for integrity metadata.

  --integrity y
  Enable default integrity settings (external).

Command variations:

lvcreate --type integrity -n Name -L Size VG
  [Uses integrity external, the default.]

lvcreate --integrity external -n Name -L Size VG
  [Uses type integrity, which is implied.]

lvcreate --integrity y -n Name -L Size VG
  [Uses integrity external, the default, and
   uses type integrity, which is implied.]

lvcreate --integrity internal -n Name -L Size VG
  [Uses type integrity, which is implied.]

lvconvert --integrity none|n LV
  [Removes external integrity.]

Options:

  --integritymetadata LV
  Use the specified LV for external metadata.
  Allows specific device placement of metadata.
  Without this option, the command creates a
  hidden LV (with an _imeta suffix) to hold the
  metadata.

  --integritysettings String
  set dm-integrity parameters, e.g. to use a journal
  instead of bitmap, --integritysettings "mode=J".

Example:

$ lvcreate --integrity external -n lvex -L1G vg
$ lvs -a vg
  LV           VG Attr       LSize  Origin
  lvex         vg -wi-a-----  1.00g [lvex_iorig]
  [lvex_imeta] vg -wi-ao---- 12.00m
  [lvex_iorig] vg -wi-ao----  1.00g

$ lvcreate --integrity internal -n lvin -L1G vg
$ lvs -a vg
  LV           VG Attr       LSize Origin
  lvin         vg -wi-a----- 1.00g [lvin_iorig]
  [lvin_iorig] vg -wi-ao---- 1.00g

Zeroing:

After a new integrity LV is created, zeroes are written to
the entire LV to initialize integrity metadata (checksums).
Without this initialization, the LV will return read errors
for any unwritten (and uninitialized) data.

A large LV may take a long time to zero.  The -Zn option can
be used to disable the whole-LV zeroing, or the lvcreate
command can be canceled while zeroing the new LV.  In either
case, the user may write to the entire LV to initialize the
integrity metadata themselves.
2019-12-04 12:27:25 -06:00
Zdenek Kabelac
f88f7c0fdc tests: add more tracing info 2019-11-15 12:37:44 +01:00
Zdenek Kabelac
496c368528 tests: reduce amount of written date
Since we reduced created LV to 4M - dd also just 4M.
2019-11-15 12:37:44 +01:00
Zdenek Kabelac
dccc50f6f6 revert "dmeventd: vdo plugin link lvm library"
This reverts commit cbabdf2fca.
and add extra comment why this code may look unused, but
in runtime is necessary.
2019-11-15 12:37:41 +01:00
David Teigland
7ea71a9eb9 Revert "hints: rewrite function"
This reverts commit 70fb31b5d6.
2019-11-14 12:15:05 -06:00
David Teigland
31a862a6be Revert "debug: enhance debug messages"
This reverts commit e92d3bd1f7.
2019-11-14 12:11:53 -06:00
Zdenek Kabelac
91df257b53 tests: enusure lib is initilized 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
e92d3bd1f7 debug: enhance debug messages 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
14e01d6316 hints: drop unneeded memset
strncpy will zero buffer itself.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1760b96368 hints: no need to check for NULL before free
free() itself checks for NULL.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
9af1d63b4d cov: use zalloc
Instead of malloc() memset() -> zalloc()
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
33c1d2e921 cov: add explicit ret value ignoring
We don't need to check for any error result codes here.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
ad0343d8cb cov: remove unused headers 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
9ee3af7efc cov: more checks for failing syscalls 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
cbabdf2fca dmeventd: vdo plugin link lvm library
Since we fixed linking of proper version of 'libdevmapper' with
linking lvm2 plugin correctly - we already have correct function
available linked with internal lvm library.
So drop unneeded include of parsing function.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1da5fd8226 cov: inline _build_desc_write
Embed function into the code, since the function is actually
simpler written this as there are no memleak troubles
with failing allocation error path.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
61a483a654 hints: check for _touch_hints
Exit when !_touch_hints().
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
c38be06531 hints: fix mem leaking buffers 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1349a52626 hints: validate allocation result 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
219fe72359 hints: validate sscanf results 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
d4d82dbb70 hints: allocate hint only when needed
Avoid mem leaking hint on every loop continue and
allocate hint only when it's going to be added into list.

Switch to use 'dm_strncpy()' and validate sizes.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
70fb31b5d6 hints: rewrite function 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1f4968289c pvck: check result of dev_get_size
Don't use garbage value for later computations.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
d67ce9e140 cov: fix mem leaking buffer
Free allocated buffer on function's exit.
Also check for fwrite() results.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
0bad3977df cov: avoid passing NULL to strstr function
When 'str1' would be NULL, there is no point to run 2nd. strstr().
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
153e55c20e cov: check for retvalue 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
44bf9c9a6a cov: fix memleak for duplicate device
For  dev_in_device_list() != 0 allocated  'devl' was
actually leaking - so instead allocate 'devl' only
when !dev_in_device_list() and indent code around.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
82e6b820b8 cov: check for NULL
Since we check for NULL pointers earlier we need
to be consistent across function - since the NULL
would applies across whole function.

When dropping 'mda' check - we are actually
already dereferencing it before - so it can't
be NULL at that places (and it's validated
before entering  _read_mda_header_and_metadata).
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
43f149526d devtype: simplify code
Update code with simpler form and check for fclose().
2019-11-14 18:06:14 +01:00
Zdenek Kabelac
33c8e4de33 cov: fix memory leak
Reapply 23cc7ddc50 to internal version
of libdm.
2019-11-14 18:05:41 +01:00
Heming Zhao
13c254fc05 fix dev_unset_last_byte after write error
dev_unset_last_byte() must be called while the fd is still valid.
After a write error, dev_unset_last_byte() must be called before
closing the dev and resetting the fd.

In the write error path, dev_unset_last_byte() was being called
after label_scan_invalidate() which meant that it would not unset
the last_byte values.

After a write error, dev_unset_last_byte() is now called in
dev_write_bytes() before label_scan_invalidate(), instead of by
the caller of dev_write_bytes().

In the common case of a successful write, the sequence is still:
dev_set_last_byte(); dev_write_bytes(); dev_unset_last_byte();

Signed-off-by: Zhao Heming <heming.zhao@suse.com>
2019-11-13 09:36:58 -06:00
45 changed files with 2024 additions and 506 deletions

View File

@@ -1745,7 +1745,8 @@ static void _init_thread_signals(void)
sigdelset(&my_sigset, SIGHUP);
sigdelset(&my_sigset, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &my_sigset, NULL);
if (pthread_sigmask(SIG_BLOCK, &my_sigset, NULL))
log_sys_error("pthread_sigmask", "SIG_BLOCK");
}
/*

View File

@@ -16,7 +16,12 @@
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
#include "daemons/dmeventd/libdevmapper-event.h"
/* Use parser from new device_mapper library */
/*
* Use parser from new device_mapper library.
* Although during compilation we can see dm_vdo_status_parse()
* in runtime we are linked agains systems libdm 'older' library
* which does not provide this symbol and plugin fails to load
*/
#include "device_mapper/vdo/status.c"
#include <sys/wait.h>

View File

@@ -392,6 +392,15 @@ struct dm_status_writecache {
int dm_get_status_writecache(struct dm_pool *mem, const char *params,
struct dm_status_writecache **status);
struct dm_status_integrity {
uint64_t number_of_mismatches;
uint64_t provided_data_sectors;
uint64_t recalc_sector;
};
int dm_get_status_integrity(struct dm_pool *mem, const char *params,
struct dm_status_integrity **status);
/*
* Parse params from STATUS call for snapshot target
*
@@ -970,6 +979,37 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
uint32_t writecache_block_size,
struct writecache_settings *settings);
struct integrity_settings {
char mode[8];
uint32_t tag_size;
const char *internal_hash;
uint32_t journal_sectors;
uint32_t interleave_sectors;
uint32_t buffer_sectors;
uint32_t journal_watermark;
uint32_t commit_time;
uint32_t block_size;
uint32_t bitmap_flush_interval;
uint64_t sectors_per_bit;
unsigned journal_sectors_set:1;
unsigned interleave_sectors_set:1;
unsigned buffer_sectors_set:1;
unsigned journal_watermark_set:1;
unsigned commit_time_set:1;
unsigned block_size_set:1;
unsigned bitmap_flush_interval_set:1;
unsigned sectors_per_bit_set:1;
int recalculate; /* not persistent */
};
int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
uint64_t size,
const char *origin_uuid,
const char *meta_uuid,
struct integrity_settings *settings);
/*
* VDO target

View File

@@ -2012,7 +2012,8 @@ static int _sysfs_get_kernel_name(uint32_t major, uint32_t minor, char *buf, siz
log_sys_error("readlink", sysfs_path);
else {
log_sys_debug("readlink", sysfs_path);
return _sysfs_find_kernel_name(major, minor, buf, buf_size);
r = _sysfs_find_kernel_name(major, minor, buf, buf_size);
goto out;
}
goto bad;
}
@@ -2033,6 +2034,7 @@ static int _sysfs_get_kernel_name(uint32_t major, uint32_t minor, char *buf, siz
strcpy(buf, name);
r = 1;
bad:
out:
free(temp_buf);
free(sysfs_path);

View File

@@ -38,6 +38,7 @@ enum {
SEG_STRIPED,
SEG_ZERO,
SEG_WRITECACHE,
SEG_INTEGRITY,
SEG_THIN_POOL,
SEG_THIN,
SEG_VDO,
@@ -78,6 +79,7 @@ static const struct {
{ SEG_STRIPED, "striped" },
{ SEG_ZERO, "zero"},
{ SEG_WRITECACHE, "writecache"},
{ SEG_INTEGRITY, "integrity"},
{ SEG_THIN_POOL, "thin-pool"},
{ SEG_THIN, "thin"},
{ SEG_VDO, "vdo" },
@@ -221,6 +223,10 @@ struct load_segment {
int writecache_pmem; /* writecache, 1 if pmem, 0 if ssd */
uint32_t writecache_block_size; /* writecache, in bytes */
struct writecache_settings writecache_settings; /* writecache */
uint64_t integrity_data_sectors; /* integrity (provided_data_sectors) */
struct dm_tree_node *integrity_meta_node; /* integrity */
struct integrity_settings integrity_settings; /* integrity */
};
/* Per-device properties */
@@ -2705,6 +2711,88 @@ static int _writecache_emit_segment_line(struct dm_task *dmt,
return 1;
}
static int _integrity_emit_segment_line(struct dm_task *dmt,
struct load_segment *seg,
char *params, size_t paramsize)
{
struct integrity_settings *set = &seg->integrity_settings;
int pos = 0;
int count;
char origin_dev[DM_FORMAT_DEV_BUFSIZE];
char meta_dev[DM_FORMAT_DEV_BUFSIZE];
if (!_build_dev_string(origin_dev, sizeof(origin_dev), seg->origin))
return_0;
if (seg->integrity_meta_node &&
!_build_dev_string(meta_dev, sizeof(meta_dev), seg->integrity_meta_node))
return_0;
count = 1; /* for internal_hash which we always pass in */
if (seg->integrity_meta_node)
count++;
if (set->journal_sectors_set)
count++;
if (set->interleave_sectors_set)
count++;
if (set->buffer_sectors_set)
count++;
if (set->journal_watermark_set)
count++;
if (set->commit_time_set)
count++;
if (set->block_size_set)
count++;
if (set->bitmap_flush_interval_set)
count++;
if (set->sectors_per_bit_set)
count++;
if (set->recalculate)
count++;
EMIT_PARAMS(pos, "%s 0 %u %s %d internal_hash:%s",
origin_dev,
set->tag_size,
set->mode,
count,
set->internal_hash);
if (seg->integrity_meta_node)
EMIT_PARAMS(pos, " meta_device:%s", meta_dev);
if (set->journal_sectors_set)
EMIT_PARAMS(pos, " journal_sectors:%u", set->journal_sectors);
if (set->interleave_sectors_set)
EMIT_PARAMS(pos, " ineterleave_sectors:%u", set->interleave_sectors);
if (set->buffer_sectors_set)
EMIT_PARAMS(pos, " buffer_sectors:%u", set->buffer_sectors);
if (set->journal_watermark_set)
EMIT_PARAMS(pos, " journal_watermark:%u", set->journal_watermark);
if (set->commit_time_set)
EMIT_PARAMS(pos, " commit_time:%u", set->commit_time);
if (set->block_size_set)
EMIT_PARAMS(pos, " block_size:%u", set->block_size);
if (set->bitmap_flush_interval_set)
EMIT_PARAMS(pos, " bitmap_flush_interval:%u", set->bitmap_flush_interval);
if (set->sectors_per_bit_set)
EMIT_PARAMS(pos, " sectors_per_bit:%llu", (unsigned long long)set->sectors_per_bit);
if (set->recalculate)
EMIT_PARAMS(pos, " recalculate");
return 1;
}
static int _thin_pool_emit_segment_line(struct dm_task *dmt,
struct load_segment *seg,
char *params, size_t paramsize)
@@ -2889,6 +2977,10 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
if (!_writecache_emit_segment_line(dmt, seg, params, paramsize))
return_0;
break;
case SEG_INTEGRITY:
if (!_integrity_emit_segment_line(dmt, seg, params, paramsize))
return_0;
break;
}
switch(seg->type) {
@@ -2901,6 +2993,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
case SEG_THIN:
case SEG_CACHE:
case SEG_WRITECACHE:
case SEG_INTEGRITY:
break;
case SEG_CRYPT:
case SEG_LINEAR:
@@ -3738,6 +3831,39 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
return 1;
}
int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
uint64_t size,
const char *origin_uuid,
const char *meta_uuid,
struct integrity_settings *settings)
{
struct load_segment *seg;
if (!(seg = _add_segment(node, SEG_INTEGRITY, size)))
return_0;
if (meta_uuid) {
if (!(seg->integrity_meta_node = dm_tree_find_node_by_uuid(node->dtree, meta_uuid))) {
log_error("Missing integrity's meta uuid %s.", meta_uuid);
return 0;
}
if (!_link_tree_nodes(node, seg->integrity_meta_node))
return_0;
}
if (!(seg->origin = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) {
log_error("Missing integrity's origin uuid %s.", origin_uuid);
return 0;
}
if (!_link_tree_nodes(node, seg->origin))
return_0;
memcpy(&seg->integrity_settings, settings, sizeof(struct integrity_settings));
return 1;
}
int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
uint64_t size,
const char *rlog_uuid,

View File

@@ -380,6 +380,35 @@ int dm_get_status_writecache(struct dm_pool *mem, const char *params,
return 1;
}
int dm_get_status_integrity(struct dm_pool *mem, const char *params,
struct dm_status_integrity **status)
{
struct dm_status_integrity *s;
char recalc_str[8];
if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_integrity))))
return_0;
memset(recalc_str, 0, sizeof(recalc_str));
if (sscanf(params, "%llu %llu %s",
(unsigned long long *)&s->number_of_mismatches,
(unsigned long long *)&s->provided_data_sectors,
recalc_str) != 3) {
log_error("Failed to parse integrity params: %s.", params);
dm_pool_free(mem, s);
return 0;
}
if (recalc_str[0] == '-')
s->recalc_sector = 0;
else
s->recalc_sector = strtoull(recalc_str, NULL, 0);
*status = s;
return 1;
}
int parse_thin_pool_status(const char *params, struct dm_status_thin_pool *s)
{
int pos;

View File

@@ -20,6 +20,7 @@ SOURCES =\
activate/activate.c \
cache/lvmcache.c \
writecache/writecache.c \
integrity/integrity.c \
cache_segtype/cache.c \
commands/toolcontext.c \
config/config.c \
@@ -67,6 +68,7 @@ SOURCES =\
log/log.c \
metadata/cache_manip.c \
metadata/writecache_manip.c \
metadata/integrity_manip.c \
metadata/lv.c \
metadata/lv_manip.c \
metadata/merge.c \

View File

@@ -2870,6 +2870,7 @@ int deactivate_lv_with_sub_lv(const struct logical_volume *lv)
int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
{
const struct logical_volume *active_lv;
const struct logical_volume *lv_use;
int ret;
/*
@@ -2888,19 +2889,30 @@ int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
goto out;
}
if (lv->status & LV_UNCOMMITTED)
lv_use = lv;
else
lv_use = lv_committed(lv);
ret = lv_activate_with_filter(cmd, NULL, 0,
(lv->status & LV_NOSCAN) ? 1 : 0,
(lv->status & LV_TEMPORARY) ? 1 : 0,
lv_committed(lv));
lv_use);
out:
return ret;
}
int deactivate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
{
const struct logical_volume *lv_use;
int ret;
ret = lv_deactivate(cmd, NULL, lv_committed(lv));
if (lv->status & LV_UNCOMMITTED)
lv_use = lv;
else
lv_use = lv_committed(lv);
ret = lv_deactivate(cmd, NULL, lv_use);
return ret;
}

View File

@@ -39,6 +39,7 @@ typedef enum {
SEG_STATUS_THIN_POOL,
SEG_STATUS_VDO_POOL,
SEG_STATUS_WRITECACHE,
SEG_STATUS_INTEGRITY,
SEG_STATUS_UNKNOWN
} lv_seg_status_type_t;
@@ -53,6 +54,7 @@ struct lv_seg_status {
struct dm_status_thin *thin;
struct dm_status_thin_pool *thin_pool;
struct dm_status_writecache *writecache;
struct dm_status_integrity *integrity;
struct lv_status_vdo vdo_pool;
};
};
@@ -260,6 +262,7 @@ void fs_unlock(void);
#define TARGET_NAME_CACHE "cache"
#define TARGET_NAME_WRITECACHE "writecache"
#define TARGET_NAME_INTEGRITY "integrity"
#define TARGET_NAME_ERROR "error"
#define TARGET_NAME_ERROR_OLD "erro" /* Truncated in older kernels */
#define TARGET_NAME_LINEAR "linear"
@@ -277,6 +280,7 @@ void fs_unlock(void);
#define MODULE_NAME_CLUSTERED_MIRROR "clog"
#define MODULE_NAME_CACHE TARGET_NAME_CACHE
#define MODULE_NAME_WRITECACHE TARGET_NAME_WRITECACHE
#define MODULE_NAME_INTEGRITY TARGET_NAME_INTEGRITY
#define MODULE_NAME_ERROR TARGET_NAME_ERROR
#define MODULE_NAME_LOG_CLUSTERED "log-clustered"
#define MODULE_NAME_LOG_USERSPACE "log-userspace"

View File

@@ -222,6 +222,10 @@ static int _get_segment_status_from_target_params(const char *target_name,
if (!dm_get_status_writecache(seg_status->mem, params, &(seg_status->writecache)))
return_0;
seg_status->type = SEG_STATUS_WRITECACHE;
} else if (segtype_is_integrity(segtype)) {
if (!dm_get_status_integrity(seg_status->mem, params, &(seg_status->integrity)))
return_0;
seg_status->type = SEG_STATUS_INTEGRITY;
} else
/*
* TODO: Add support for other segment types too!
@@ -299,6 +303,9 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
if (lv_is_vdo_pool(seg_status->seg->lv))
length = get_vdo_pool_virtual_size(seg_status->seg);
if (lv_is_integrity(seg_status->seg->lv))
length = seg_status->seg->integrity_data_sectors;
do {
target = dm_get_next_target(dmt, target, &target_start,
&target_length, &target_name, &target_params);
@@ -2620,6 +2627,10 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
if (!_add_lv_to_dtree(dm, dtree, seg->writecache, dm->activation ? origin_only : 1))
return_0;
}
if (seg->integrity_meta_dev && seg_is_integrity(seg)) {
if (!_add_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, dm->activation ? origin_only : 1))
return_0;
}
if (seg->pool_lv &&
(lv_is_cache_pool(seg->pool_lv) || lv_is_cache_vol(seg->pool_lv) || dm->track_external_lv_deps) &&
/* When activating and not origin_only detect linear 'overlay' over pool */
@@ -3076,6 +3087,11 @@ static int _add_segment_to_dtree(struct dev_manager *dm,
lv_layer(seg->writecache)))
return_0;
if (seg->integrity_meta_dev && !laopts->origin_only &&
!_add_new_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, laopts,
lv_layer(seg->integrity_meta_dev)))
return_0;
/* Add any LVs used by this segment */
for (s = 0; s < seg->area_count; ++s) {
if ((seg_type(seg, s) == AREA_LV) &&

30
lib/cache/lvmcache.c vendored
View File

@@ -21,7 +21,6 @@
#include "lib/locking/locking.h"
#include "lib/metadata/metadata.h"
#include "lib/mm/memlock.h"
#include "lib/datastruct/str_list.h"
#include "lib/format_text/format-text.h"
#include "lib/config/config.h"
@@ -1952,24 +1951,25 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller,
strncpy(dev->pvid, pvid_s, sizeof(dev->pvid));
/*
* Keep the existing PV/dev in lvmcache, and save the
* new duplicate in the list of duplicates. After
* scanning is complete, compare the duplicate devs
* with those in lvmcache to check if one of the
* duplicates is preferred and if so switch lvmcache to
* use it.
*/
if (!(devl = zalloc(sizeof(*devl))))
return_NULL;
devl->dev = dev;
/* shouldn't happen */
if (dev_in_device_list(dev, &_initial_duplicates))
log_debug_cache("Initial duplicate already in list %s", dev_name(dev));
else
else {
/*
* Keep the existing PV/dev in lvmcache, and save the
* new duplicate in the list of duplicates. After
* scanning is complete, compare the duplicate devs
* with those in lvmcache to check if one of the
* duplicates is preferred and if so switch lvmcache to
* use it.
*/
if (!(devl = zalloc(sizeof(*devl))))
return_NULL;
devl->dev = dev;
dm_list_add(&_initial_duplicates, &devl->list);
}
if (is_duplicate)
*is_duplicate = 1;

View File

@@ -1362,6 +1362,9 @@ static int _init_segtypes(struct cmd_context *cmd)
return 0;
#endif
if (!init_integrity_segtypes(cmd, &seglib))
return 0;
return 1;
}

View File

@@ -42,7 +42,6 @@ int dev_is_pmem(struct device *dev)
{
FILE *fp;
char path[PATH_MAX];
char buffer[64];
int is_pmem = 0;
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/queue/dax",
@@ -56,27 +55,16 @@ int dev_is_pmem(struct device *dev)
if (!(fp = fopen(path, "r")))
return 0;
if (!fgets(buffer, sizeof(buffer), fp)) {
log_warn("Failed to read %s.", path);
if (fclose(fp))
log_sys_debug("fclose", path);
return 0;
} else if (sscanf(buffer, "%d", &is_pmem) != 1) {
log_warn("Failed to parse %s '%s'.", path, buffer);
if (fclose(fp))
log_sys_debug("fclose", path);
return 0;
}
if (fscanf(fp, "%d", &is_pmem) != 1)
log_warn("Failed to parse DAX %s.", path);
if (is_pmem)
log_debug("%s is pmem", dev_name(dev));
if (fclose(fp))
log_sys_debug("fclose", path);
if (is_pmem) {
log_debug("%s is pmem", dev_name(dev));
return 1;
}
return 0;
return is_pmem ? 1 : 0;
}
int dev_is_lv(struct device *dev)
@@ -84,6 +72,7 @@ int dev_is_lv(struct device *dev)
FILE *fp;
char path[PATH_MAX];
char buffer[64];
int ret = 0;
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/dm/uuid",
dm_sysfs_dir(),
@@ -96,17 +85,15 @@ int dev_is_lv(struct device *dev)
if (!(fp = fopen(path, "r")))
return 0;
if (!fgets(buffer, sizeof(buffer), fp)) {
if (!fgets(buffer, sizeof(buffer), fp))
log_warn("Failed to read %s.", path);
fclose(fp);
return 0;
}
else if (!strncmp(buffer, "LVM-", 4))
ret = 1;
fclose(fp);
if (fclose(fp))
log_sys_debug("fclose", path);
if (!strncmp(buffer, "LVM-", 4))
return 1;
return 0;
return ret;
}
struct dev_types *create_dev_types(const char *proc_dir,

View File

@@ -104,8 +104,11 @@ static const struct flag _lv_flags[] = {
{LV_VDO_POOL, NULL, 0},
{LV_VDO_POOL_DATA, NULL, 0},
{WRITECACHE, NULL, 0},
{INTEGRITY, NULL, 0},
{INTEGRITY_METADATA, NULL, 0},
{LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */
{LV_REMOVED, NULL, 0},
{LV_UNCOMMITTED, NULL, 0},
{0, NULL, 0}
};

View File

@@ -277,7 +277,6 @@ static int _raw_write_mda_header(const struct format_type *fmt,
dev_set_last_byte(dev, start_byte + MDA_HEADER_SIZE);
if (!dev_write_bytes(dev, start_byte, MDA_HEADER_SIZE, mdah)) {
dev_unset_last_byte(dev);
log_error("Failed to write mda header to %s fd %d", dev_name(dev), dev->bcache_fd);
return 0;
}
@@ -342,7 +341,7 @@ static struct raw_locn *_read_metadata_location_vg(struct device_area *dev_area,
* Don't try to check existing metadata
* if given vgname is an empty string.
*/
if (!*vgname)
if (!vgname || !*vgname)
return rlocn;
/*
@@ -532,29 +531,6 @@ static struct volume_group *_vg_read_precommit_raw(struct format_instance *fid,
return vg;
}
#define MAX_DESC_LEN 2048
static char *_build_desc_write(struct cmd_context *cmd, struct volume_group *vg)
{
size_t len = strlen(cmd->cmd_line) + 32;
char *desc;
if (len > MAX_DESC_LEN)
len = MAX_DESC_LEN;
if (!(desc = zalloc(len)))
return_NULL;
vg->write_count++;
if (vg->write_count == 1)
dm_snprintf(desc, len, "Write from %s.", cmd->cmd_line);
else
dm_snprintf(desc, len, "Write[%u] from %s.", vg->write_count, cmd->cmd_line);
return desc;
}
/*
* VG metadata updates:
*
@@ -599,6 +575,7 @@ static char *_build_desc_write(struct cmd_context *cmd, struct volume_group *vg)
static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
char desc[2048];
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
struct raw_locn *rlocn_old;
@@ -673,12 +650,15 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
write_buf_size = fidtc->write_buf_size;
new_size = fidtc->new_metadata_size;
} else {
char *desc = _build_desc_write(fid->fmt->cmd, vg);
if (!vg->write_count++)
(void) dm_snprintf(desc, sizeof(desc), "Write from %s.", vg->cmd->cmd_line);
else
(void) dm_snprintf(desc, sizeof(desc), "Write[%u] from %s.", vg->write_count, vg->cmd->cmd_line);
new_size = text_vg_export_raw(vg, desc, &write_buf, &write_buf_size);
fidtc->write_buf = write_buf;
fidtc->write_buf_size = write_buf_size;
fidtc->new_metadata_size = new_size;
free(desc);
}
if (!new_size || !write_buf) {
@@ -989,7 +969,6 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
if (!dev_write_bytes(mdac->area.dev, write1_start, (size_t)write1_size, write_buf)) {
log_error("Failed to write metadata to %s fd %d", devname, mdac->area.dev->bcache_fd);
dev_unset_last_byte(mdac->area.dev);
goto out;
}
@@ -1002,7 +981,6 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
if (!dev_write_bytes(mdac->area.dev, write2_start, write2_size,
write_buf + new_size - new_wrap)) {
log_error("Failed to write metadata wrap to %s fd %d", devname, mdac->area.dev->bcache_fd);
dev_unset_last_byte(mdac->area.dev);
goto out;
}
}

View File

@@ -332,14 +332,12 @@ static int _read_mda_header_and_metadata(const struct format_type *fmt,
log_warn("WARNING: bad metadata header on %s at %llu.",
dev_name(mdac->area.dev),
(unsigned long long)mdac->area.start);
if (mda)
mda->header_start = mdac->area.start;
mda->header_start = mdac->area.start;
*bad_fields |= BAD_MDA_HEADER;
return 0;
}
if (mda)
mda->header_start = mdah->start;
mda->header_start = mdah->start;
mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));

328
lib/integrity/integrity.c Normal file
View File

@@ -0,0 +1,328 @@
/*
* Copyright (C) 2013-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/metadata/segtype.h"
#include "lib/display/display.h"
#include "lib/format_text/text_export.h"
#include "lib/config/config.h"
#include "lib/datastruct/str_list.h"
#include "lib/misc/lvm-string.h"
#include "lib/activate/activate.h"
#include "lib/metadata/metadata.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/config/defaults.h"
#define SEG_LOG_ERROR(t, p...) \
log_error(t " segment %s of logical volume %s.", ## p, \
dm_config_parent_name(sn), seg->lv->name), 0;
static void _integrity_display(const struct lv_segment *seg)
{
/* TODO: lvdisplay segments */
}
static int _integrity_text_import(struct lv_segment *seg,
const struct dm_config_node *sn,
struct dm_hash_table *pv_hash __attribute__((unused)))
{
struct integrity_settings *set;
struct logical_volume *origin_lv = NULL;
struct logical_volume *meta_lv = NULL;
const char *origin_name = NULL;
const char *meta_dev = NULL;
const char *mode = NULL;
const char *hash = NULL;
memset(&seg->integrity_settings, 0, sizeof(struct integrity_settings));
set = &seg->integrity_settings;
/* origin always set */
if (!dm_config_has_node(sn, "origin"))
return SEG_LOG_ERROR("origin not specified in");
if (!dm_config_get_str(sn, "origin", &origin_name))
return SEG_LOG_ERROR("origin must be a string in");
if (!(origin_lv = find_lv(seg->lv->vg, origin_name)))
return SEG_LOG_ERROR("Unknown LV specified for integrity origin %s in", origin_name);
if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0))
return_0;
/* data_sectors always set */
if (!dm_config_get_uint64(sn, "data_sectors", &seg->integrity_data_sectors))
return SEG_LOG_ERROR("integrity data_sectors must be set in");
/* mode always set */
if (!dm_config_get_str(sn, "mode", &mode))
return SEG_LOG_ERROR("integrity mode must be set in");
if (strlen(mode) > 7)
return SEG_LOG_ERROR("integrity mode invalid in");
strncpy(set->mode, mode, 7);
/* tag_size always set */
if (!dm_config_get_uint32(sn, "tag_size", &set->tag_size))
return SEG_LOG_ERROR("integrity tag_size must be set in");
/* internal_hash always set */
if (!dm_config_get_str(sn, "internal_hash", &hash))
return SEG_LOG_ERROR("integrity internal_hash must be set in");
if (!(set->internal_hash = strdup(hash)))
return SEG_LOG_ERROR("integrity internal_hash failed to be set in");
/* meta_dev optional */
if (dm_config_has_node(sn, "meta_dev")) {
if (!dm_config_get_str(sn, "meta_dev", &meta_dev))
return SEG_LOG_ERROR("meta_dev must be a string in");
if (!(meta_lv = find_lv(seg->lv->vg, meta_dev)))
return SEG_LOG_ERROR("Unknown logical volume %s specified for integrity in", meta_dev);
}
/* the rest are optional */
if (dm_config_has_node(sn, "journal_sectors")) {
if (!dm_config_get_uint32(sn, "journal_sectors", &set->journal_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->journal_sectors_set = 1;
}
if (dm_config_has_node(sn, "interleave_sectors")) {
if (!dm_config_get_uint32(sn, "interleave_sectors", &set->interleave_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->interleave_sectors_set = 1;
}
if (dm_config_has_node(sn, "buffer_sectors")) {
if (!dm_config_get_uint32(sn, "buffer_sectors", &set->buffer_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->buffer_sectors_set = 1;
}
if (dm_config_has_node(sn, "journal_watermark")) {
if (!dm_config_get_uint32(sn, "journal_watermark", &set->journal_watermark))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->journal_watermark_set = 1;
}
if (dm_config_has_node(sn, "commit_time")) {
if (!dm_config_get_uint32(sn, "commit_time", &set->commit_time))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->commit_time_set = 1;
}
if (dm_config_has_node(sn, "block_size")) {
if (!dm_config_get_uint32(sn, "block_size", &set->block_size))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->block_size_set = 1;
}
if (dm_config_has_node(sn, "bitmap_flush_interval")) {
if (!dm_config_get_uint32(sn, "bitmap_flush_interval", &set->bitmap_flush_interval))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->bitmap_flush_interval_set = 1;
}
if (dm_config_has_node(sn, "sectors_per_bit")) {
if (!dm_config_get_uint64(sn, "sectors_per_bit", &set->sectors_per_bit))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->sectors_per_bit_set = 1;
}
seg->origin = origin_lv;
seg->integrity_meta_dev = meta_lv;
seg->lv->status |= INTEGRITY;
if (meta_lv)
meta_lv->status |= INTEGRITY_METADATA;
if (meta_lv && !add_seg_to_segs_using_this_lv(meta_lv, seg))
return_0;
return 1;
}
static int _integrity_text_import_area_count(const struct dm_config_node *sn,
uint32_t *area_count)
{
*area_count = 1;
return 1;
}
static int _integrity_text_export(const struct lv_segment *seg,
struct formatter *f)
{
const struct integrity_settings *set = &seg->integrity_settings;
outf(f, "origin = \"%s\"", seg_lv(seg, 0)->name);
outf(f, "data_sectors = %llu", (unsigned long long)seg->integrity_data_sectors);
outf(f, "mode = \"%s\"", set->mode);
outf(f, "tag_size = %u", set->tag_size);
outf(f, "internal_hash = \"%s\"", set->internal_hash);
if (seg->integrity_meta_dev)
outf(f, "meta_dev = \"%s\"", seg->integrity_meta_dev->name);
if (set->journal_sectors_set)
outf(f, "journal_sectors = %u", set->journal_sectors);
if (set->interleave_sectors_set)
outf(f, "interleave_sectors = %u", set->interleave_sectors);
if (set->buffer_sectors_set)
outf(f, "buffer_sectors = %u", set->buffer_sectors);
if (set->journal_watermark_set)
outf(f, "journal_watermark = %u", set->journal_watermark);
if (set->commit_time_set)
outf(f, "commit_time = %u", set->commit_time);
if (set->block_size_set)
outf(f, "block_size = %u", set->block_size);
if (set->bitmap_flush_interval)
outf(f, "bitmap_flush_interval = %u", set->bitmap_flush_interval);
if (set->sectors_per_bit)
outf(f, "sectors_per_bit = %llu", (unsigned long long)set->sectors_per_bit);
return 1;
}
static void _destroy(struct segment_type *segtype)
{
free((void *) segtype);
}
#ifdef DEVMAPPER_SUPPORT
static int _target_present(struct cmd_context *cmd,
const struct lv_segment *seg __attribute__((unused)),
unsigned *attributes __attribute__((unused)))
{
static int _integrity_checked = 0;
static int _integrity_present = 0;
if (!activation())
return 0;
if (!_integrity_checked) {
_integrity_checked = 1;
_integrity_present = target_present(cmd, TARGET_NAME_INTEGRITY, 0);
}
return _integrity_present;
}
static int _modules_needed(struct dm_pool *mem,
const struct lv_segment *seg __attribute__((unused)),
struct dm_list *modules)
{
if (!str_list_add(mem, modules, MODULE_NAME_INTEGRITY)) {
log_error("String list allocation failed for integrity module.");
return 0;
}
return 1;
}
#endif /* DEVMAPPER_SUPPORT */
#ifdef DEVMAPPER_SUPPORT
static int _integrity_add_target_line(struct dev_manager *dm,
struct dm_pool *mem,
struct cmd_context *cmd __attribute__((unused)),
void **target_state __attribute__((unused)),
struct lv_segment *seg,
const struct lv_activate_opts *laopts __attribute__((unused)),
struct dm_tree_node *node, uint64_t len,
uint32_t *pvmove_mirror_count __attribute__((unused)))
{
char *origin_uuid;
char *meta_uuid = NULL;
if (!seg_is_integrity(seg)) {
log_error(INTERNAL_ERROR "Passed segment is not integrity.");
return 0;
}
if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0), NULL)))
return_0;
if (seg->integrity_meta_dev) {
if (!(meta_uuid = build_dm_uuid(mem, seg->integrity_meta_dev, NULL)))
return_0;
}
if (!seg->integrity_data_sectors) {
log_error("_integrity_add_target_line zero size");
return_0;
}
if (!dm_tree_node_add_integrity_target(node, seg->integrity_data_sectors,
origin_uuid, meta_uuid,
&seg->integrity_settings))
return_0;
return 1;
}
#endif /* DEVMAPPER_SUPPORT */
static struct segtype_handler _integrity_ops = {
.display = _integrity_display,
.text_import = _integrity_text_import,
.text_import_area_count = _integrity_text_import_area_count,
.text_export = _integrity_text_export,
#ifdef DEVMAPPER_SUPPORT
.add_target_line = _integrity_add_target_line,
.target_present = _target_present,
.modules_needed = _modules_needed,
#endif
.destroy = _destroy,
};
int init_integrity_segtypes(struct cmd_context *cmd,
struct segtype_library *seglib)
{
struct segment_type *segtype = zalloc(sizeof(*segtype));
if (!segtype) {
log_error("Failed to allocate memory for integrity segtype");
return 0;
}
segtype->name = SEG_TYPE_NAME_INTEGRITY;
segtype->flags = SEG_INTEGRITY;
segtype->ops = &_integrity_ops;
if (!lvm_register_segtype(seglib, segtype))
return_0;
log_very_verbose("Initialised segtype: %s", segtype->name);
return 1;
}

View File

@@ -638,7 +638,8 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
char devpath[PATH_MAX];
FILE *fp;
struct dev_iter *iter;
struct hint *hint;
struct hint hint;
struct hint *alloc_hint;
struct device *dev;
char *split[HINT_LINE_WORDS];
char *name, *pvid, *devn, *vgname, *p, *filter_str = NULL;
@@ -662,11 +663,7 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
split[i] = NULL;
while (fgets(_hint_line, sizeof(_hint_line), fp)) {
if (!(hint = zalloc(sizeof(struct hint)))) {
ret = 0;
break;
}
memset(&hint, 0, sizeof(hint));
if (_hint_line[0] == '#')
continue;
@@ -704,13 +701,11 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
_filter_to_str(cmd, devices_global_filter_CFG, &filter_str);
if (!filter_str || strcmp(filter_str, _hint_line + keylen)) {
log_debug("ignore hints with different global_filter");
if (filter_str)
free(filter_str);
free(filter_str);
*needs_refresh = 1;
break;
}
if (filter_str)
free(filter_str);
free(filter_str);
continue;
}
@@ -719,23 +714,20 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
_filter_to_str(cmd, devices_filter_CFG, &filter_str);
if (!filter_str || strcmp(filter_str, _hint_line + keylen)) {
log_debug("ignore hints with different filter");
if (filter_str)
free(filter_str);
free(filter_str);
*needs_refresh = 1;
break;
}
if (filter_str)
free(filter_str);
free(filter_str);
continue;
}
keylen = strlen("scan_lvs:");
if (!strncmp(_hint_line, "scan_lvs:", keylen)) {
int scan_lvs = 0;
sscanf(_hint_line + keylen, "%u", &scan_lvs);
if (scan_lvs != cmd->scan_lvs) {
log_debug("ignore hints with different scan_lvs");
if ((sscanf(_hint_line + keylen, "%u", &scan_lvs) != 1) ||
scan_lvs != cmd->scan_lvs) {
log_debug("ignore hints with different or unreadable scan_lvs");
*needs_refresh = 1;
break;
}
@@ -744,7 +736,11 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
keylen = strlen("devs_hash:");
if (!strncmp(_hint_line, "devs_hash:", keylen)) {
sscanf(_hint_line + keylen, "%u %u", &read_hash, &read_count);
if (sscanf(_hint_line + keylen, "%u %u", &read_hash, &read_count) != 2) {
log_debug("ignore hints with invalid devs_hash");
*needs_refresh = 1;
break;
}
continue;
}
@@ -764,19 +760,28 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
vgname = split[3];
if (name && !strncmp(name, "scan:", 5))
strncpy(hint->name, name+5, PATH_MAX);
if (!dm_strncpy(hint.name, name + 5, sizeof(hint.name)))
continue;
if (pvid && !strncmp(pvid, "pvid:", 5))
strncpy(hint->pvid, pvid+5, ID_LEN);
if (!dm_strncpy(hint.pvid, pvid + 5, sizeof(hint.pvid)))
continue;
if (devn && sscanf(devn, "devn:%d:%d", &major, &minor) == 2)
hint->devt = makedev(major, minor);
hint.devt = makedev(major, minor);
if (vgname && (strlen(vgname) > 3) && (vgname[4] != '-'))
strncpy(hint->vgname, vgname+3, NAME_LEN);
if (!dm_strncpy(hint.vgname, vgname + 3, sizeof(hint.vgname)))
continue;
log_debug("add hint %s %s %d:%d %s", hint->name, hint->pvid, major, minor, vgname);
dm_list_add(hints, &hint->list);
if (!(alloc_hint = malloc(sizeof(struct hint)))) {
ret = 0;
break;
}
memcpy(alloc_hint, &hint, sizeof(hint));
log_debug("add hint %s %s %d:%d %s", hint.name, hint.pvid, major, minor, vgname);
dm_list_add(hints, &alloc_hint->list);
found++;
}
@@ -911,13 +916,11 @@ int write_hint_file(struct cmd_context *cmd, int newhints)
_filter_to_str(cmd, devices_global_filter_CFG, &filter_str);
fprintf(fp, "global_filter:%s\n", filter_str ?: "-");
if (filter_str)
free(filter_str);
free(filter_str);
_filter_to_str(cmd, devices_filter_CFG, &filter_str);
fprintf(fp, "filter:%s\n", filter_str ?: "-");
if (filter_str)
free(filter_str);
free(filter_str);
fprintf(fp, "scan_lvs:%d\n", cmd->scan_lvs);
@@ -951,7 +954,6 @@ int write_hint_file(struct cmd_context *cmd, int newhints)
* detect when the devices on the system change, which
* invalidates the existing hints.
*/
memset(devpath, 0, sizeof(devpath));
strncpy(devpath, dev_name(dev), PATH_MAX);
hash = calc_crc(hash, (const uint8_t *)devpath, strlen(devpath));
count++;
@@ -1179,7 +1181,8 @@ static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
if (!(st = strchr(arg, '/'))) {
/* simple vgname */
name = strdup(arg);
if (!(name = strdup(arg)))
return;
goto check;
}
@@ -1187,7 +1190,8 @@ static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
for (p = arg; p < st; p++)
namebuf[i++] = *p;
name = strdup(namebuf);
if (!(name = strdup(namebuf)))
return;
check:
/*
@@ -1202,6 +1206,8 @@ check:
return;
}
}
free(name);
}
/*
@@ -1261,8 +1267,9 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
*/
if (_newhints_exists()) {
log_debug("get_hints: newhints file");
if (!_hints_exists())
_touch_hints();
if (!_hints_exists() && !_touch_hints())
return 0;
if (!_lock_hints(cmd, LOCK_EX, NONBLOCK))
return 0;
/* create new hints after scan */
@@ -1355,6 +1362,9 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
dm_list_size(devs_out), dm_list_size(devs_in));
dm_list_splice(hints_out, &hints_list);
free(vgname);
return 1;
}

View File

@@ -218,7 +218,7 @@ int label_write(struct device *dev, struct label *label)
if (!dev_write_bytes(dev, offset, LABEL_SIZE, buf)) {
log_debug_devs("Failed to write label to %s", dev_name(dev));
r = 0;
return 0;
}
dev_unset_last_byte(dev);
@@ -252,8 +252,6 @@ struct label *label_create(struct labeller *labeller)
/* global variable for accessing the bcache populated by label scan */
struct bcache *scan_bcache;
#define BCACHE_BLOCK_SIZE_IN_SECTORS 256 /* 256*512 = 128K */
static bool _in_bcache(struct device *dev)
{
if (!dev)
@@ -655,7 +653,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
int submit_count;
int scan_failed;
int is_lvm_device;
int error;
int ret;
dm_list_init(&wait_devs);
@@ -702,12 +699,11 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
dm_list_iterate_items_safe(devl, devl2, &wait_devs) {
bb = NULL;
error = 0;
scan_failed = 0;
is_lvm_device = 0;
if (!bcache_get(scan_bcache, devl->dev->bcache_fd, 0, 0, &bb)) {
log_debug_devs("Scan failed to read %s error %d.", dev_name(devl->dev), error);
log_debug_devs("Scan failed to read %s.", dev_name(devl->dev));
scan_failed = 1;
scan_read_errors++;
scan_failed_count++;
@@ -1451,6 +1447,7 @@ bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data)
if (!bcache_write_bytes(scan_bcache, dev->bcache_fd, start, len, data)) {
log_error("Error writing device %s at %llu length %u.",
dev_name(dev), (unsigned long long)start, (uint32_t)len);
dev_unset_last_byte(dev);
label_scan_invalidate(dev);
return false;
}
@@ -1458,6 +1455,7 @@ bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data)
if (!bcache_flush(scan_bcache)) {
log_error("Error writing device %s at %llu length %u.",
dev_name(dev), (unsigned long long)start, (uint32_t)len);
dev_unset_last_byte(dev);
label_scan_invalidate(dev);
return false;
}

View File

@@ -118,6 +118,14 @@ int label_scan_open(struct device *dev);
int label_scan_open_excl(struct device *dev);
int label_scan_open_rw(struct device *dev);
/*
* These are the sizes the label.c uses to set up
* and use bcache (they are not bcache restrictions
* or defs.)
*/
#define BCACHE_BLOCK_SIZE_IN_SECTORS 256 /* 256*512 = 128K */
#define BCACHE_BLOCK_SIZE_IN_BYTES 131072
/*
* Wrappers around bcache equivalents.
* (these make it easier to disable bcache and revert to direct rw if needed)

View File

@@ -23,7 +23,6 @@
#include "lib/config/defaults.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/misc/lvm-signal.h"
#include "lib/activate/dev_manager.h"
/* https://github.com/jthornber/thin-provisioning-tools/blob/master/caching/cache_metadata_size.cc */
#define DM_TRANSACTION_OVERHEAD 4096 /* KiB */

View File

@@ -0,0 +1,662 @@
/*
* Copyright (C) 2014-2015 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib/misc/lib.h"
#include "lib/metadata/metadata.h"
#include "lib/locking/locking.h"
#include "lib/misc/lvm-string.h"
#include "lib/commands/toolcontext.h"
#include "lib/display/display.h"
#include "lib/metadata/segtype.h"
#include "lib/activate/activate.h"
#include "lib/config/defaults.h"
#include "lib/activate/dev_manager.h"
#define DEFAULT_TAG_SIZE 4 /* bytes */
#define DEFAULT_MODE 'B'
#define DEFAULT_INTERNAL_HASH "crc32c"
#define ONE_MB_IN_BYTES 1048576
int lv_is_integrity_origin(const struct logical_volume *lv)
{
struct seg_list *sl;
dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
if (!sl->seg || !sl->seg->lv || !sl->seg->origin)
continue;
if (lv_is_integrity(sl->seg->lv) && (sl->seg->origin == lv))
return 1;
}
return 0;
}
/*
* Every 500M of data needs 4M of metadata.
* (From trial and error testing.)
*/
static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes)
{
return ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES);
}
/*
* The user wants external metadata, but did not specify an existing
* LV to hold metadata, so create an LV for metadata. Save it in
* lp->integrity_meta_lv and it will be passed to lv_add_integrity().
*/
int lv_create_integrity_metadata(struct cmd_context *cmd,
struct volume_group *vg,
struct lvcreate_params *lp)
{
char metaname[NAME_LEN];
uint64_t lv_size_bytes, meta_bytes, meta_sectors;
struct logical_volume *lv;
struct lvcreate_params lp_meta = {
.activate = CHANGE_AN,
.alloc = ALLOC_INHERIT,
.major = -1,
.minor = -1,
.permission = LVM_READ | LVM_WRITE,
.pvh = &vg->pvs,
.read_ahead = DM_READ_AHEAD_NONE,
.stripes = 1,
.vg_name = vg->name,
.zero = 0,
.wipe_signatures = 0,
.suppress_zero_warn = 1,
};
if (dm_snprintf(metaname, NAME_LEN, "%s_imeta", lp->lv_name) < 0) {
log_error("Failed to create metadata LV name.");
return 0;
}
if (!(lp_meta.lv_name = strdup(metaname)))
return_0;
lp_meta.pvh = lp->pvh;
lv_size_bytes = lp->extents * vg->extent_size * 512;
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes);
meta_sectors = meta_bytes / 512;
lp_meta.extents = meta_sectors / vg->extent_size;
log_print("Creating integrity metadata LV %s with size %s.",
metaname, display_size(cmd, meta_sectors));
dm_list_init(&lp_meta.tags);
if (!(lp_meta.segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
if (!(lv = lv_create_single(vg, &lp_meta))) {
log_error("Failed to create integrity metadata LV");
return 0;
}
lp->integrity_meta_lv = lv;
return 1;
}
static int _get_provided_data_sectors(struct logical_volume *lv, uint64_t *provided_data_sectors)
{
struct lv_with_info_and_seg_status status;
uint64_t data_sectors, extra_sectors;
memset(&status, 0, sizeof(status));
status.seg_status.type = SEG_STATUS_NONE;
status.seg_status.seg = first_seg(lv);
/* FIXME: why reporter_pool? */
if (!(status.seg_status.mem = dm_pool_create("reporter_pool", 1024))) {
log_error("Failed to get mem for LV status.");
return 0;
}
if (!lv_info_with_seg_status(lv->vg->cmd, first_seg(lv), &status, 1, 1)) {
log_error("Failed to get device mapper status for %s", display_lvname(lv));
goto fail;
}
if (!status.info.exists) {
log_error("No device mapper info exists for %s", display_lvname(lv));
goto fail;
}
if (status.seg_status.type != SEG_STATUS_INTEGRITY) {
log_error("Invalid device mapper status type (%d) for %s",
(uint32_t)status.seg_status.type, display_lvname(lv));
goto fail;
}
data_sectors = status.seg_status.integrity->provided_data_sectors;
if ((extra_sectors = (data_sectors % lv->vg->extent_size))) {
data_sectors -= extra_sectors;
log_debug("Reduce provided_data_sectors by %llu to %llu for extent alignment",
(unsigned long long)extra_sectors, (unsigned long long)data_sectors);
}
*provided_data_sectors = data_sectors;
dm_pool_destroy(status.seg_status.mem);
return 1;
fail:
dm_pool_destroy(status.seg_status.mem);
return 0;
}
int lv_remove_integrity(struct logical_volume *lv)
{
struct lv_segment *seg = first_seg(lv);
struct logical_volume *origin;
struct logical_volume *meta_lv;
if (!seg_is_integrity(seg)) {
log_error("LV %s segment is not integrity.", display_lvname(lv));
return 0;
}
if (!(meta_lv = seg->integrity_meta_dev)) {
log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
return 0;
}
if (!(origin = seg_lv(seg, 0))) {
log_error("LV %s integrity segment has no origin", display_lvname(lv));
return 0;
}
if (!remove_seg_from_segs_using_this_lv(seg->integrity_meta_dev, seg))
return_0;
lv_set_visible(seg->integrity_meta_dev);
lv->status &= ~INTEGRITY;
meta_lv->status &= ~INTEGRITY_METADATA;
seg->integrity_meta_dev = NULL;
if (!remove_layer_from_lv(lv, origin))
return_0;
if (!lv_remove(origin))
return_0;
if (!lv_remove(meta_lv))
log_warn("WARNING: failed to remove integrity metadata LV.");
return 1;
}
/*
* Add integrity to each raid image.
*
* for each rimage_N:
* . create and allocate a new linear LV rimage_N_imeta
* . move the segments from rimage_N to a new rimage_N_iorig
* . add an integrity segment to rimage_N with
* origin=rimage_N_iorig, meta_dev=rimage_N_imeta
*
* Before:
* rimage_0
* segment1: striped: pv0:A
* rimage_1
* segment1: striped: pv1:B
*
* After:
* rimage_0
* segment1: integrity: rimage_0_iorig, rimage_0_imeta
* rimage_1
* segment1: integrity: rimage_1_iorig, rimage_1_imeta
* rimage_0_iorig
* segment1: striped: pv0:A
* rimage_1_iorig
* segment1: striped: pv1:B
* rimage_0_imeta
* segment1: striped: pv2:A
* rimage_1_imeta
* segment1: striped: pv2:B
*
*/
static int _lv_add_integrity_to_raid(struct logical_volume *lv, const char *arg,
struct integrity_settings *settings,
struct dm_list *pvh,
uint64_t *zero_data_sectors)
{
struct lvcreate_params lp;
struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
struct cmd_context *cmd = lv->vg->cmd;
struct volume_group *vg = lv->vg;
struct logical_volume *lv_image, *lv_imeta, *lv_iorig;
struct lv_segment *seg_top, *seg_image;
const struct segment_type *segtype;
struct integrity_settings *set;
uint64_t lv_image_size; /* sectors */
uint64_t status_data_sectors;
uint32_t area_count, s;
int internal_metadata;
int ret = 1;
memset(imeta_lvs, 0, sizeof(imeta_lvs));
if (dm_list_size(&lv->segments) != 1)
return_0;
seg_top = first_seg(lv);
area_count = seg_top->area_count;
if ((internal_metadata = !strcmp(arg, "internal"))) {
/*
* FIXME: raid on internal integrity might not be used widely
* enough to enable, given the additional complexity/support.
* i.e. nearly everyone may just use external metadata.
*
* FIXME: _info_run() needs code to adjust the length, like
* is done for if (lv_is_integrity()) length = ...
*/
/* goto skip_imeta; */
log_error("Internal integrity metadata is not yet supported with raid.");
return 0;
}
/*
* For each rimage, create an _imeta LV for integrity metadata.
* Each needs to be zeroed.
*/
for (s = 0; s < area_count; s++) {
struct logical_volume *meta_lv;
struct wipe_params wipe;
if (s >= DEFAULT_RAID_MAX_IMAGES)
return_0;
lv_image = seg_lv(seg_top, s);
if (!seg_is_striped(first_seg(lv_image))) {
log_error("raid1 image must be linear to add integrity");
return_0;
}
/*
* allocate a new linear LV NAME_rimage_N_imeta
* lv_create_integrity_metadata() returns its result in lp
*/
memset(&lp, 0, sizeof(lp));
lp.lv_name = lv_image->name;
lp.pvh = pvh;
lp.extents = lv_image->size / vg->extent_size;
if (!lv_create_integrity_metadata(cmd, vg, &lp)) {
return_0;
}
meta_lv = lp.integrity_meta_lv;
/*
* dm-integrity requires the metadata LV header to be zeroed.
*/
if (!activate_lv(cmd, meta_lv)) {
log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
return 0;
}
memset(&wipe, 0, sizeof(wipe));
wipe.do_zero = 1;
wipe.zero_sectors = 8;
if (!wipe_lv(meta_lv, wipe)) {
log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
return 0;
}
if (!deactivate_lv(cmd, meta_lv)) {
log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
return 0;
}
/* Used below to set up the new integrity segment. */
imeta_lvs[s] = meta_lv;
}
/* skip_imeta: */
/*
* For each rimage, move its segments to a new rimage_iorig and give
* the rimage a new integrity segment.
*/
for (s = 0; s < area_count; s++) {
lv_image = seg_lv(seg_top, s);
lv_image_size = lv_image->size; /* sectors */
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
return_0;
log_debug("Adding integrity to raid image %s", lv_image->name);
/*
* "lv_iorig" is a new LV with new id, but with the segments
* from "lv_image". "lv_image" keeps the existing name and id,
* but gets a new integrity segment, in place of the segments
* that were moved to lv_iorig.
*/
if (!(lv_iorig = insert_layer_for_lv(cmd, lv_image, INTEGRITY, "_iorig")))
return_0;
lv_image->status |= INTEGRITY;
/*
* Set up the new first segment of lv_image as integrity.
*/
seg_image = first_seg(lv_image);
seg_image->segtype = segtype;
if (internal_metadata) {
/* dm-integrity wants temp/fake size of 1 to report usable size */
lv_image->size = 1;
seg_image->integrity_data_sectors = 1;
} else {
lv_imeta = imeta_lvs[s]; /* external metadata lv created above */
lv_imeta->status |= INTEGRITY_METADATA;
lv_set_hidden(lv_imeta);
seg_image->integrity_data_sectors = lv_image_size;
seg_image->integrity_meta_dev = lv_imeta;
}
memcpy(&seg_image->integrity_settings, settings, sizeof(struct integrity_settings));
set = &seg_image->integrity_settings;
if (!set->mode[0])
set->mode[0] = DEFAULT_MODE;
if (!set->tag_size)
set->tag_size = DEFAULT_TAG_SIZE;
if (!set->internal_hash)
set->internal_hash = DEFAULT_INTERNAL_HASH;
set->recalculate = 1; /* enable integrity initialization in the kernel */
}
if (internal_metadata) {
/* Get the size from the first image, the others will be the same. */
lv_image = seg_lv(seg_top, 0);
lv_image->status |= LV_TEMPORARY;
lv_image->status |= LV_NOSCAN;
lv_image->status |= LV_UNCOMMITTED;
log_debug("Activating temporary integrity LV to get data sectors.");
if (!activate_lv(cmd, lv_image)) {
log_error("Failed to activate temporary integrity.");
ret = 0;
goto out;
}
if (!_get_provided_data_sectors(lv_image, &status_data_sectors)) {
log_error("Failed to get data sectors from dm-integrity");
ret = 0;
} else {
log_print("Found integrity provided_data_sectors %llu", (unsigned long long)status_data_sectors);
ret = 1;
}
if (!status_data_sectors) {
log_error("LV size too small to include metadata.");
ret = 0;
}
lv_image->status |= LV_UNCOMMITTED;
if (!deactivate_lv(cmd, lv_image)) {
log_error("Failed to deactivate temporary integrity.");
ret = 0;
}
if (!ret)
goto_out;
lv_image->status &= ~LV_UNCOMMITTED;
lv_image->status &= ~LV_NOSCAN;
lv_image->status &= ~LV_TEMPORARY;
for (s = 0; s < area_count; s++) {
lv_image = seg_lv(seg_top, s);
lv_image->size = lv_image_size; /* was 1 temporarily */
seg_image = first_seg(lv_image);
seg_image->integrity_data_sectors = status_data_sectors;
}
}
/*
* recalculate above enables automatic integrity metadata intialization
* in the kernel when first activated, instead of activating the raid
* LV and writing zeroes to the entire thing from the lvcreate command
* to initialize the metadata in the integrity images.
*
* Zeroing the raid LV to intialize integrity metadata on the images
* does not work because dm-raid immediately reads the integrity
* images when activated, and gets errors back because of no init.
*
* When activated with recalculate, dm-integrity will not return errors
* to dm-raid when it reads uninitialized blocks.
*/
*zero_data_sectors = 0; /* disables intialization by lvcreate */
log_debug("Write VG with integrity added to LV");
if (!vg_write(vg) || !vg_commit(vg))
ret = 0;
out:
return ret;
}
int lv_add_integrity(struct logical_volume *lv, const char *arg,
struct logical_volume *meta_lv_created,
const char *meta_name,
struct integrity_settings *settings,
struct dm_list *pvh,
uint64_t *zero_data_sectors)
{
struct cmd_context *cmd = lv->vg->cmd;
struct volume_group *vg = lv->vg;
struct integrity_settings *set;
struct logical_volume *lv_orig;
struct logical_volume *meta_lv = NULL;
const struct segment_type *segtype;
struct lv_segment *seg;
uint64_t meta_bytes, meta_sectors;
uint64_t lv_size_sectors;
int ret = 1;
if (lv_is_raid(lv))
return _lv_add_integrity_to_raid(lv, arg, settings, pvh, zero_data_sectors);
lv_size_sectors = lv->size;
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
return_0;
/*
* "lv_orig" is a new LV with new id, but with the segments from "lv".
* "lv" keeps the existing name and id, but gets a new integrity segment,
* in place of the segments that were moved to lv_orig.
*/
if (!(lv_orig = insert_layer_for_lv(cmd, lv, INTEGRITY, "_iorig")))
return_0;
seg = first_seg(lv);
seg->segtype = segtype;
lv->status |= INTEGRITY;
memcpy(&seg->integrity_settings, settings, sizeof(struct integrity_settings));
set = &seg->integrity_settings;
if (!set->mode[0])
set->mode[0] = DEFAULT_MODE;
if (!set->tag_size)
set->tag_size = DEFAULT_TAG_SIZE;
if (!set->internal_hash)
set->internal_hash = DEFAULT_INTERNAL_HASH;
/*
* --integrity <arg> is y|external|internal
*/
if (!arg)
arg = "external";
if (!strcmp(arg, "none")) {
log_error("Invalid --integrity arg for lvcreate.");
return 0;
}
if (!strcmp(arg, "internal") && meta_name) {
log_error("Internal integrity cannot be used with integritymetadata option.");
return 0;
}
if (meta_lv_created)
meta_lv = meta_lv_created;
else if (meta_name) {
if (!(meta_lv = find_lv(vg, meta_name))) {
log_error("LV %s not found.", meta_name);
return_0;
}
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv->size * 512);
meta_sectors = meta_bytes / 512;
if (meta_lv->size < meta_sectors) {
log_error("Integrity metadata needs %s, metadata LV is only %s.",
display_size(cmd, meta_sectors), display_size(cmd, meta_lv->size));
return 0;
}
}
/*
* When not using a meta_dev, dm-integrity needs to tell us what the
* usable size of the LV is, which is the dev size minus the integrity
* overhead. To find that, we need to do a special, temporary activation
* of the new LV, specifying a dm dev size of 1, then check the dm dev
* status field provided_data_sectors, which is the actual size of the
* LV. We need to include provided_data_sectors in the metadata for the
* new LV, and use this as the dm dev size for normal LV activation.
*
* When using a meta_dev, the dm dev size is the size of the data
* device. The necessary size of the meta_dev for the given data
* device needs to be estimated.
*/
if (meta_lv) {
struct wipe_params wipe;
if (!sync_local_dev_names(cmd)) {
log_error("Failed to sync local devices.");
return 0;
}
log_verbose("Zeroing LV for integrity metadata");
if (!lv_is_active(meta_lv)) {
if (!activate_lv(cmd, meta_lv)) {
log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
return 0;
}
}
memset(&wipe, 0, sizeof(wipe));
wipe.do_zero = 1;
wipe.zero_sectors = 8;
if (!wipe_lv(meta_lv, wipe)) {
log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
return 0;
}
if (!deactivate_lv(cmd, meta_lv)) {
log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
return 0;
}
meta_lv->status |= INTEGRITY_METADATA;
seg->integrity_data_sectors = lv_size_sectors;
seg->integrity_meta_dev = meta_lv;
lv_set_hidden(meta_lv);
/* TODO: give meta_lv a suffix? e.g. _imeta */
} else {
/* dm-integrity wants temp/fake size of 1 to report usable size */
seg->integrity_data_sectors = 1;
lv->status |= LV_TEMPORARY;
lv->status |= LV_NOSCAN;
lv->status |= LV_UNCOMMITTED;
log_debug("Activating temporary integrity LV to get data sectors.");
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate temporary integrity.");
ret = 0;
goto out;
}
if (!_get_provided_data_sectors(lv, &seg->integrity_data_sectors)) {
log_error("Failed to get data sectors from dm-integrity");
ret = 0;
} else {
log_debug("Found integrity provided_data_sectors %llu", (unsigned long long)seg->integrity_data_sectors);
ret = 1;
}
if (!seg->integrity_data_sectors) {
log_error("LV size too small to include metadata.");
ret = 0;
}
lv->status |= LV_UNCOMMITTED;
if (!deactivate_lv(cmd, lv)) {
log_error("Failed to deactivate temporary integrity.");
ret = 0;
}
lv->status &= ~LV_UNCOMMITTED;
lv->status &= ~LV_NOSCAN;
lv->status &= ~LV_TEMPORARY;
}
/*
* Tells lvcreate to zero the final LV at the end of commands.
*/
*zero_data_sectors = seg->integrity_data_sectors;
log_debug("Write VG with integrity added to LV");
if (!vg_write(vg) || !vg_commit(vg))
ret = 0;
out:
return ret;
}

View File

@@ -585,6 +585,8 @@ struct logical_volume *lv_origin_lv(const struct logical_volume *lv)
origin = first_seg(lv)->external_lv;
else if (lv_is_writecache(lv) && first_seg(lv)->origin)
origin = first_seg(lv)->origin;
else if (lv_is_integrity(lv) && first_seg(lv)->origin)
origin = first_seg(lv)->origin;
return origin;
}
@@ -1200,10 +1202,13 @@ char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_
repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
else if (lv_is_pool_metadata(lv) ||
lv_is_pool_metadata_spare(lv) ||
lv_is_raid_metadata(lv))
lv_is_raid_metadata(lv) ||
lv_is_integrity_metadata(lv))
repstr[0] = 'e';
else if (lv_is_cache_type(lv) || lv_is_writecache(lv))
repstr[0] = 'C';
else if (lv_is_integrity(lv))
repstr[0] = 'g';
else if (lv_is_raid(lv))
repstr[0] = (lv_is_not_synced(lv)) ? 'R' : 'r';
else if (lv_is_mirror(lv))

View File

@@ -28,6 +28,7 @@
#include "lib/datastruct/str_list.h"
#include "lib/config/defaults.h"
#include "lib/misc/lvm-exec.h"
#include "lib/misc/lvm-signal.h"
#include "lib/mm/memlock.h"
#include "lib/locking/lvmlockd.h"
#include "lib/label/label.h"
@@ -55,6 +56,8 @@ typedef enum {
#define A_POSITIONAL_FILL 0x40 /* Slots are positional and filled using PREFERRED */
#define A_PARTITION_BY_TAGS 0x80 /* No allocated area may share any tag with any other */
#define ONE_MB_IN_BYTES 1048576
/*
* Constant parameters during a single allocation attempt.
*/
@@ -134,7 +137,9 @@ enum {
LV_TYPE_SANLOCK,
LV_TYPE_CACHEVOL,
LV_TYPE_WRITECACHE,
LV_TYPE_WRITECACHEORIGIN
LV_TYPE_WRITECACHEORIGIN,
LV_TYPE_INTEGRITY,
LV_TYPE_INTEGRITYORIGIN
};
static const char *_lv_type_names[] = {
@@ -190,6 +195,8 @@ static const char *_lv_type_names[] = {
[LV_TYPE_CACHEVOL] = "cachevol",
[LV_TYPE_WRITECACHE] = "writecache",
[LV_TYPE_WRITECACHEORIGIN] = "writecacheorigin",
[LV_TYPE_INTEGRITY] = "integrity",
[LV_TYPE_INTEGRITYORIGIN] = "integrityorigin",
};
static int _lv_layout_and_role_mirror(struct dm_pool *mem,
@@ -461,6 +468,43 @@ bad:
return 0;
}
static int _lv_layout_and_role_integrity(struct dm_pool *mem,
const struct logical_volume *lv,
struct dm_list *layout,
struct dm_list *role,
int *public_lv)
{
int top_level = 0;
/* non-top-level LVs */
if (lv_is_integrity_metadata(lv)) {
if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_METADATA]))
goto_bad;
} else if (lv_is_integrity_origin(lv)) {
if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_ORIGIN]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITYORIGIN]))
goto_bad;
} else
top_level = 1;
if (!top_level) {
*public_lv = 0;
return 1;
}
/* top-level LVs */
if (lv_is_integrity(lv)) {
if (!str_list_add_no_dup_check(mem, layout, _lv_type_names[LV_TYPE_INTEGRITY]))
goto_bad;
}
return 1;
bad:
return 0;
}
static int _lv_layout_and_role_thick_origin_snapshot(struct dm_pool *mem,
const struct logical_volume *lv,
struct dm_list *layout,
@@ -577,6 +621,11 @@ int lv_layout_and_role(struct dm_pool *mem, const struct logical_volume *lv,
!_lv_layout_and_role_cache(mem, lv, *layout, *role, &public_lv))
goto_bad;
/* Integrity related */
if ((lv_is_integrity(lv) || lv_is_integrity_origin(lv) || lv_is_integrity_metadata(lv)) &&
!_lv_layout_and_role_integrity(mem, lv, *layout, *role, &public_lv))
goto_bad;
/* VDO and related */
if (lv_is_vdo_type(lv) &&
!_lv_layout_and_role_vdo(mem, lv, *layout, *role, &public_lv))
@@ -1457,6 +1506,15 @@ static int _lv_reduce(struct logical_volume *lv, uint32_t extents, int delete)
return_0;
}
if (delete && seg_is_integrity(seg)) {
/* Remove integrity origin in addition to integrity layer. */
if (!lv_remove(seg_lv(seg, 0)))
return_0;
/* Remove integrity metadata. */
if (seg->integrity_meta_dev && !lv_remove(seg->integrity_meta_dev))
return_0;
}
if ((pool_lv = seg->pool_lv)) {
if (!detach_pool_lv(seg))
return_0;
@@ -5654,6 +5712,11 @@ int lv_resize(struct logical_volume *lv,
return 0;
}
if (lv_is_integrity(lv)) {
log_error("Resize not yet allowed on LVs with integrity.");
return 0;
}
if (!_lvresize_check(lv, lp))
return_0;
@@ -7410,6 +7473,90 @@ int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
return 1;
}
int zero_lv_name(struct cmd_context *cmd, const char *vg_name, const char *lv_name, uint64_t lv_size_sectors)
{
char name[PATH_MAX];
struct device *dev;
uint64_t off = 0, i = 0, j = 0;
uint64_t lv_size_bytes = lv_size_sectors * 512;
uint64_t zero_bytes;
uint32_t extra_bytes;
if (dm_snprintf(name, sizeof(name), "%s%s/%s", cmd->dev_dir, vg_name, lv_name) < 0) {
log_error("Device path name too long, device not zeroed (%s).", lv_name);
return 0;
}
if (!(dev = dev_cache_get(cmd, name, NULL))) {
log_error("Failed to find device %s: device not zeroed.", name);
return 0;
}
if (!label_scan_open_rw(dev)) {
log_error("Failed to open %s: device not zeroed.", name);
return 0;
}
zero_bytes = lv_size_bytes;
log_print("Zeroing %s %s... (cancel command to zero manually)",
name, display_size(cmd, zero_bytes/512));
if ((extra_bytes = (zero_bytes % ONE_MB_IN_BYTES)))
zero_bytes -= extra_bytes;
/*
* Write 1MiB at a time to avoid going over bcache size.
* Then write 128KiB (bcache block sizes) at a time to
* cover remaining dev size.
*/
sigint_allow();
for (i = 0; i < (zero_bytes / ONE_MB_IN_BYTES); i++) {
off = i * ONE_MB_IN_BYTES;
if (!dev_write_zeros(dev, off, (size_t)ONE_MB_IN_BYTES))
log_error("Failed to zero LV at offset %llu.", (unsigned long long)off);
if (off && !(off % (1024 * ONE_MB_IN_BYTES)))
log_print("Zeroed %s...", display_size(cmd, off/512));
if (sigint_caught()) {
log_print("Zeroing canceled.");
goto out;
}
}
if (extra_bytes) {
log_debug("Zeroing final %u bytes at %llu.", extra_bytes, (unsigned long long)off);
for (j = 0; j < (extra_bytes / BCACHE_BLOCK_SIZE_IN_BYTES); j++) {
off = i * ONE_MB_IN_BYTES + j * BCACHE_BLOCK_SIZE_IN_BYTES;
if (!dev_write_zeros(dev, off, (size_t)BCACHE_BLOCK_SIZE_IN_BYTES))
log_error("Failed to zero LV at offset %llu.", (unsigned long long)off);
if (sigint_caught()) {
log_print("Zeroing canceled.");
goto out;
}
}
}
/*
* FIXME: bcache can't write partial blocks yet.
* This shouldn't actually happen given current
* usage where LV size is a multiple of extents.
*/
if ((extra_bytes = lv_size_bytes - (i * ONE_MB_IN_BYTES + j * BCACHE_BLOCK_SIZE_IN_BYTES)))
log_warn("WARNING: last %llu bytes not zeroed.", (unsigned long long)extra_bytes);
out:
sigint_restore();
label_scan_invalidate(dev);
return 1;
}
/*
* Initialize the LV with 'value'.
*/
@@ -7679,6 +7826,15 @@ static int _should_wipe_lv(struct lvcreate_params *lp,
first_seg(first_seg(lv)->pool_lv)->zero_new_blocks))
return 0;
/*
* dm-integrity requires the first sector (integrity superblock) to be
* zero when creating a new integrity device.
* TODO: print a warning or error if the user specifically
* asks for no wiping or zeroing?
*/
if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg))
return 1;
/* Cannot zero read-only volume */
if ((lv->status & LVM_WRITE) &&
(lp->zero || lp->wipe_signatures))
@@ -7934,6 +8090,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
/* FIXME Eventually support raid/mirrors with -m */
if (!(create_segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
} else if (seg_is_integrity(lp)) {
if (!(create_segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
} else if (seg_is_mirrored(lp) || (seg_is_raid(lp) && !seg_is_any_raid0(lp))) {
if (!(lp->region_size = adjusted_mirror_region_size(vg->cmd,
vg->extent_size,
@@ -8277,6 +8438,19 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
goto revert_new_lv;
}
lv->status &= ~LV_TEMPORARY;
} else if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg)) {
/*
* Activate the new origin LV so it can be zeroed/wiped
* below before adding integrity.
*/
lv->status |= LV_TEMPORARY;
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate new LV for wiping.");
goto revert_new_lv;
}
lv->status &= ~LV_TEMPORARY;
} else if (!lv_active_change(cmd, lv, lp->activate)) {
log_error("Failed to activate new LV %s.", display_lvname(lv));
goto deactivate_and_revert_new_lv;
@@ -8296,6 +8470,53 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
}
}
if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg)) {
log_verbose("Adding integrity to new LV");
/* Origin is active from zeroing, deactivate to add integrity. */
if (!deactivate_lv(cmd, lv)) {
log_error("Failed to deactivate LV to add integrity");
goto revert_new_lv;
}
if (!lv_add_integrity(lv, lp->integrity_arg, lp->integrity_meta_lv,
lp->integrity_meta_name, &lp->integrity_settings, lp->pvh,
&lp->zero_data_sectors))
goto revert_new_lv;
backup(vg);
/*
* The standard option combination should be -Zy -ay, in which
* case we activate here, and zero at the end of the command.
* The invalid combination -Zy -an has been prevented earlier.
* The combination -Zn -an involves no zeroing or activation.
* For combination -Zn -ay we activate here.
*/
if (lp->zero) {
/* Activate for zeroing at the end of lvcreate. */
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate LV %s.", display_lvname(lv));
goto out;
}
} else if (!lp->zero && is_change_activating(lp->activate)) {
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate LV %s.", display_lvname(lv));
goto out;
}
}
/*
* The entire LV is zeroed, which can take a long time,
* so defer this to the end of the command when no locks
* are held, and the command can be canceled without
* problems (if the user doesn't want to wait, or wants
* to do the zeroing themselves.)
*/
goto out;
}
if (seg_is_vdo_pool(lp)) {
if (!convert_vdo_pool_lv(lv, &lp->vdo_params, &lp->virtual_extents)) {
stack;

View File

@@ -742,6 +742,8 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
seg_found++;
if (seg->metadata_lv == lv || seg->pool_lv == lv || seg->writecache == lv)
seg_found++;
if (seg->integrity_meta_dev == lv)
seg_found++;
if (seg_is_thin_volume(seg) && (seg->origin == lv || seg->external_lv == lv))
seg_found++;

View File

@@ -84,12 +84,15 @@
#define CONVERTING UINT64_C(0x0000000000400000) /* LV */
#define MISSING_PV UINT64_C(0x0000000000800000) /* PV */
#define INTEGRITY UINT64_C(0x0000000000800000) /* LV - Internal use only */
#define PV_MOVED_VG UINT64_C(0x4000000000000000) /* PV - Moved to a new VG */
#define PARTIAL_LV UINT64_C(0x0000000001000000) /* LV - derived flag, not
written out in metadata*/
//#define POSTORDER_FLAG UINT64_C(0x0000000002000000) /* Not real flags, reserved for
//#define POSTORDER_OPEN_FLAG UINT64_C(0x0000000004000000) temporary use inside vg_read_internal. */
#define INTEGRITY_METADATA UINT64_C(0x0000000004000000) /* LV - Internal use only */
#define LV_UNCOMMITTED UINT64_C(0x0000000002000000)
#define VIRTUAL_ORIGIN UINT64_C(0x0000000008000000) /* LV - internal use only */
#define MERGING UINT64_C(0x0000000010000000) /* LV SEG */
@@ -261,6 +264,8 @@
#define lv_is_pool_metadata_spare(lv) (((lv)->status & POOL_METADATA_SPARE) ? 1 : 0)
#define lv_is_lockd_sanlock_lv(lv) (((lv)->status & LOCKD_SANLOCK_LV) ? 1 : 0)
#define lv_is_writecache(lv) (((lv)->status & WRITECACHE) ? 1 : 0)
#define lv_is_integrity(lv) (((lv)->status & INTEGRITY) ? 1 : 0)
#define lv_is_integrity_metadata(lv) (((lv)->status & INTEGRITY_METADATA) ? 1 : 0)
#define lv_is_vdo(lv) (((lv)->status & LV_VDO) ? 1 : 0)
#define lv_is_vdo_pool(lv) (((lv)->status & LV_VDO_POOL) ? 1 : 0)
@@ -272,9 +277,11 @@
/* Recognize component LV (matching lib/misc/lvm-string.c _lvname_has_reserved_component_string()) */
#define lv_is_component(lv) (lv_is_cache_origin(lv) || \
lv_is_writecache_origin(lv) || \
lv_is_integrity_origin(lv) || \
((lv)->status & (\
CACHE_POOL_DATA |\
CACHE_POOL_METADATA |\
INTEGRITY_METADATA |\
LV_CACHE_VOL |\
LV_VDO_POOL_DATA |\
MIRROR_IMAGE |\
@@ -519,6 +526,10 @@ struct lv_segment {
uint32_t writecache_block_size; /* For writecache */
struct writecache_settings writecache_settings; /* For writecache */
uint64_t integrity_data_sectors;
struct logical_volume *integrity_meta_dev;
struct integrity_settings integrity_settings;
struct dm_vdo_target_params vdo_params; /* For VDO-pool */
uint32_t vdo_pool_header_size; /* For VDO-pool */
uint32_t vdo_pool_virtual_extents; /* For VDO-pool */
@@ -992,6 +1003,12 @@ struct lvcreate_params {
alloc_policy_t alloc; /* all */
struct dm_vdo_target_params vdo_params; /* vdo */
const char *integrity_arg;
const char *integrity_meta_name; /* external LV is user-specified */
struct logical_volume *integrity_meta_lv; /* external LV we create */
struct integrity_settings integrity_settings;
uint64_t zero_data_sectors; /* the resulting size that should be zeroed at the end */
struct dm_list tags; /* all */
int yes;
@@ -1086,6 +1103,8 @@ int lv_is_cache_origin(const struct logical_volume *lv);
int lv_is_writecache_origin(const struct logical_volume *lv);
int lv_is_writecache_cachevol(const struct logical_volume *lv);
int lv_is_integrity_origin(const struct logical_volume *lv);
int lv_is_merging_cow(const struct logical_volume *cow);
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size);
int cow_has_min_chunks(const struct volume_group *vg, uint32_t cow_extents, uint32_t chunk_size);
@@ -1385,4 +1404,16 @@ int vg_is_foreign(struct volume_group *vg);
void vg_write_commit_bad_mdas(struct cmd_context *cmd, struct volume_group *vg);
int lv_add_integrity(struct logical_volume *lv, const char *arg,
struct logical_volume *meta_lv_created,
const char *meta_name, struct integrity_settings *settings,
struct dm_list *pvh,
uint64_t *zero_data_sectors);
int lv_create_integrity_metadata(struct cmd_context *cmd,
struct volume_group *vg,
struct lvcreate_params *lp);
int lv_remove_integrity(struct logical_volume *lv);
int zero_lv_name(struct cmd_context *cmd, const char *vg_name, const char *lv_name, uint64_t lv_size_sectors);
#endif

View File

@@ -266,7 +266,6 @@ static int _write_log_header(struct cmd_context *cmd, struct logical_volume *lv)
dev_set_last_byte(dev, sizeof(log_header));
if (!dev_write_bytes(dev, UINT64_C(0), sizeof(log_header), &log_header)) {
dev_unset_last_byte(dev);
log_error("Failed to write log header to %s.", name);
return 0;
}

View File

@@ -67,6 +67,7 @@ struct dev_manager;
#define SEG_RAID6_N_6 (1ULL << 35)
#define SEG_RAID6 SEG_RAID6_ZR
#define SEG_WRITECACHE (1ULL << 36)
#define SEG_INTEGRITY (1ULL << 37)
#define SEG_STRIPED_TARGET (1ULL << 39)
#define SEG_LINEAR_TARGET (1ULL << 40)
@@ -84,6 +85,7 @@ struct dev_manager;
#define SEG_TYPE_NAME_CACHE "cache"
#define SEG_TYPE_NAME_CACHE_POOL "cache-pool"
#define SEG_TYPE_NAME_WRITECACHE "writecache"
#define SEG_TYPE_NAME_INTEGRITY "integrity"
#define SEG_TYPE_NAME_ERROR "error"
#define SEG_TYPE_NAME_FREE "free"
#define SEG_TYPE_NAME_ZERO "zero"
@@ -117,6 +119,7 @@ struct dev_manager;
#define segtype_is_cache(segtype) ((segtype)->flags & SEG_CACHE ? 1 : 0)
#define segtype_is_cache_pool(segtype) ((segtype)->flags & SEG_CACHE_POOL ? 1 : 0)
#define segtype_is_writecache(segtype) ((segtype)->flags & SEG_WRITECACHE ? 1 : 0)
#define segtype_is_integrity(segtype) ((segtype)->flags & SEG_INTEGRITY ? 1 : 0)
#define segtype_is_mirrored(segtype) ((segtype)->flags & SEG_AREAS_MIRRORED ? 1 : 0)
#define segtype_is_mirror(segtype) ((segtype)->flags & SEG_MIRROR ? 1 : 0)
#define segtype_is_pool(segtype) ((segtype)->flags & (SEG_CACHE_POOL | SEG_THIN_POOL) ? 1 : 0)
@@ -179,6 +182,7 @@ struct dev_manager;
#define seg_is_cache(seg) segtype_is_cache((seg)->segtype)
#define seg_is_cache_pool(seg) segtype_is_cache_pool((seg)->segtype)
#define seg_is_writecache(seg) segtype_is_writecache((seg)->segtype)
#define seg_is_integrity(seg) segtype_is_integrity((seg)->segtype)
#define seg_is_used_cache_pool(seg) (seg_is_cache_pool(seg) && (!dm_list_empty(&(seg->lv)->segs_using_this_lv)))
#define seg_is_linear(seg) (seg_is_striped(seg) && ((seg)->area_count == 1))
#define seg_is_mirror(seg) segtype_is_mirror((seg)->segtype)
@@ -347,6 +351,8 @@ int init_vdo_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
int init_writecache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
int init_integrity_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
#define CACHE_FEATURE_POLICY_MQ (1U << 0)
#define CACHE_FEATURE_POLICY_SMQ (1U << 1)
#define CACHE_FEATURE_METADATA2 (1U << 2)

View File

@@ -503,11 +503,11 @@ int update_pool_lv(struct logical_volume *lv, int activate)
* which Node has pool active.
*/
if (!activate_lv(lv->vg->cmd, lv)) {
init_dmeventd_monitor(monitored);
(void) init_dmeventd_monitor(monitored);
return_0;
}
if (!lv_is_active(lv)) {
init_dmeventd_monitor(monitored);
(void) init_dmeventd_monitor(monitored);
log_error("Cannot activate thin pool %s, perhaps skipped in lvm.conf volume_list?",
display_lvname(lv));
return 0;

View File

@@ -21,8 +21,6 @@
#include "lib/metadata/segtype.h"
#include "lib/activate/activate.h"
#include "lib/config/defaults.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/misc/lvm-signal.h"
#include "lib/activate/dev_manager.h"
int lv_is_writecache_origin(const struct logical_volume *lv)

View File

@@ -240,6 +240,26 @@ static int _raid_text_export(const struct lv_segment *seg, struct formatter *f)
return _raid_text_export_raid(seg, f);
}
static int _image_integrity_data_sectors(struct lv_segment *seg, uint64_t *data_sectors)
{
struct logical_volume *lv_image;
struct lv_segment *seg_image;
int s;
*data_sectors = 0;
for (s = 0; s < seg->area_count; s++) {
lv_image = seg_lv(seg, s);
if (lv_is_integrity(lv_image)) {
seg_image = first_seg(lv_image);
*data_sectors = seg_image->integrity_data_sectors;
return 1;
}
}
return 1;
}
static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
struct dm_pool *mem __attribute__((unused)),
struct cmd_context *cmd __attribute__((unused)),
@@ -256,6 +276,7 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
uint64_t writemostly[RAID_BITMAP_SIZE] = { 0 };
struct dm_tree_node_raid_params_v2 params = { 0 };
unsigned attrs;
uint64_t integrity_data_sectors = 0;
if (seg_is_raid4(seg)) {
if (!_raid_target_present(cmd, NULL, &attrs) ||
@@ -351,6 +372,15 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
params.stripe_size = seg->stripe_size;
params.flags = flags;
if (!_image_integrity_data_sectors(seg, &integrity_data_sectors))
return_0;
if (integrity_data_sectors) {
log_debug("Reducing raid size from %llu to integrity_data_sectors %llu",
(unsigned long long)len, (unsigned long long)integrity_data_sectors);
len = integrity_data_sectors;
}
if (!dm_tree_node_add_raid_target_with_params_v2(node, len, &params))
return_0;

View File

@@ -656,7 +656,8 @@ void daemon_start(daemon_state s)
failed = 1; /* FD out of available selectable set */
sigfillset(&new_set);
sigprocmask(SIG_SETMASK, NULL, &old_set);
if (sigprocmask(SIG_SETMASK, NULL, &old_set))
perror("sigprocmask error");
while (!failed) {
_reset_timeout(s);

View File

@@ -315,7 +315,7 @@ lib/dm-version-expected: $(top_srcdir)/VERSION_DM .lib-dir-stamp
CMDS = lvm $(shell cat $(top_builddir)/tools/.commands 2>/dev/null)
LIB = $(addprefix lib/, $(LIB_SECURETEST) $(LIB_DMSECURETEST) $(LIB_SHARED) $(LIB_LOCAL) $(LIB_NOT) $(LIB_LINK_NOT) $(LIB_FLAVOURS))
.tests-stamp: $(ALL) $(LIB) $(SUBDIRS) lib/version-expected lib/dm-version-expected unit-test
.tests-stamp: .lib-dir-stamp $(ALL) $(LIB) $(SUBDIRS) lib/version-expected lib/dm-version-expected unit-test
@echo " [TEST-STAMP]"
@if test "$(srcdir)" != . ; then \
echo "Linking tests to builddir."; \

View File

@@ -56,7 +56,7 @@ mount "$DM_DEV_DIR/$vg/$lv1" "$mount_dir"
echo 3 >/proc/sys/vm/drop_caches
# FIXME: This is filling up ram disk. Use sane amount of data please! Rate limit the data written!
dd if=/dev/urandom of="$mount_dir/random" bs=1M count=50 conv=fdatasync
dd if=/dev/urandom of="$mount_dir/random" bs=1M count=4 conv=fdatasync
checksum_ "$mount_dir/random" >MD5
# FIXME: wait_for_sync - is this really testing anything under load?

View File

@@ -74,6 +74,8 @@ check active $vg mirror12
vgchange -a n $vg
aux enable_dev "$dev3"
aux disable_dev "$dev4"
dmsetup table
dmsetup info -c
vgchange -aey $vg
not vgck $vg

View File

@@ -230,9 +230,6 @@ arg(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0,
"(Also see dm-thin-pool kernel module option no_space_timeout.)\n"
"See \\fBlvmthin\\fP(7) for more information.\n")
arg(file_long_ARG, '\0', "file", string_VAL, 0, 0,
"File name.\n")
arg(force_long_ARG, '\0', "force", 0, ARG_COUNTABLE, 0,
"Force metadata restore even with thin pool LVs.\n"
"Use with extreme caution. Most changes to thin metadata\n"
@@ -275,6 +272,15 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", 0, 0, 0,
"and \\fBdiff\\fP types include unsupported settings in their output by default,\n"
"all the other types ignore unsupported settings.\n")
arg(integrity_ARG, '\0', "integrity", string_VAL, 0, 0,
"Controls if integrity metadata should be stored and checked for an LV.\n")
arg(integritymetadata_ARG, '\0', "integritymetadata", lv_VAL, 0, 0,
"The name of an LV to hold integrity metadata.\n")
arg(integritysettings_ARG, '\0', "integritysettings", string_VAL, ARG_GROUPABLE, 0,
"Set dm-integrity parameters.\n")
arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0,
"By default the PV is labelled with an LVM2 identifier in its second\n"
"sector (sector 1). This lets you use a different sector near the\n"

View File

@@ -719,12 +719,12 @@ FLAGS: SECONDARY_SYNTAX
# and the LV type is known.
lvconvert --repair LV_cache_cachepool_mirror_raid_thinpool
OO: --usepolicies, --interval Number, --poolmetadataspare Bool, --file_long String, OO_LVCONVERT
OO: --usepolicies, --interval Number, --poolmetadataspare Bool, OO_LVCONVERT
OP: PV ...
ID: lvconvert_repair
DESC: Replace failed PVs in a raid or mirror LV.
DESC: Repair a thin pool.
DESC: Repair a cache pool or cache.
DESC: Repair a cache pool.
RULE: all not lv_is_locked lv_is_pvmove
RULE: --poolmetadataspare and LV_cache LV_cachepool LV_thinpool
@@ -757,6 +757,14 @@ FLAGS: SECONDARY_SYNTAX
---
lvconvert --integrity String LV
OO: OO_LVCONVERT
OP: PV ...
ID: lvconvert_integrity
DESC: Remove integrity from an LV.
---
# --extents is not specified; it's an automatic alternative for --size
OO_LVCREATE: --addtag Tag, --alloc Alloc, --autobackup Bool, --activate Active,
@@ -870,7 +878,8 @@ DESC: Create a raid1 or mirror LV (infers --type raid1|mirror).
# R9,R10,R11,R12 (--type raid with any use of --stripes/--mirrors)
lvcreate --type raid --size SizeMB VG
OO: --mirrors PNumber, --stripes Number, --stripesize SizeKB,
--regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB, OO_LVCREATE
--regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
--integrity String, --integritysettings String, OO_LVCREATE
OP: PV ...
ID: lvcreate_raid_any
DESC: Create a raid LV (a specific raid level must be used, e.g. raid1).
@@ -1269,6 +1278,20 @@ FLAGS: SECONDARY_SYNTAX
---
lvcreate --type integrity --size SizeMB VG
OO: --integrity String, --integritymetadata LV, --integritysettings String, OO_LVCREATE
OP: PV ...
ID: lvcreate_integrity
DESC: Create a LV with integrity.
lvcreate --integrity String --size SizeMB VG
OO: --type integrity, --integritymetadata LV, --integritysettings String, OO_LVCREATE
OP: PV ...
ID: lvcreate_integrity
DESC: Create a LV with integrity (infers --type integrity).
---
lvdisplay
OO: --aligned, --all, --binary, --colon, --columns,
--configreport ConfigReport, --foreign, --history, --ignorelockingfailure,

View File

@@ -34,5 +34,6 @@ lvt(raid10_LVT, "raid10", NULL)
lvt(error_LVT, "error", NULL)
lvt(zero_LVT, "zero", NULL)
lvt(writecache_LVT, "writecache", NULL)
lvt(integrity_LVT, "integrity", NULL)
lvt(LVT_COUNT, "", NULL)

View File

@@ -2480,339 +2480,10 @@ deactivate_pmslv:
return 1;
}
/*
* Use cache_repair to read metadata from cachevol_lv and
* write repaired metadata to new file.
/* TODO: lots of similar code with thinpool repair
* investigate possible better code sharing...
*/
static int _run_cache_repair_from_cachevol(struct cmd_context *cmd,
struct logical_volume *cachevol_lv,
const char *meta_file)
{
char cachevol_lv_path[PATH_MAX];
char *cachevol_lv_name;
const char *dmdir = dm_dir();
const char *cache_repair = find_config_tree_str_allow_empty(cmd, global_cache_repair_executable_CFG, NULL);
const struct dm_config_node *cn;
const struct dm_config_value *cv;
const char *argv[MAX_PDATA_ARGS + 7]; /* Max supported args */
int args = 0;
int status;
if (!cache_repair || !cache_repair[0]) {
log_error("No cache_repair command set in lvm.conf. Repair is disabled.");
return 0;
}
if (!(cachevol_lv_name = dm_build_dm_name(cmd->mem, cachevol_lv->vg->name, cachevol_lv->name, NULL))) {
log_error("Failed to create dm name for %s.", display_lvname(cachevol_lv));
return 0;
}
if (dm_snprintf(cachevol_lv_path, sizeof(cachevol_lv_path), "%s/%s", dmdir, cachevol_lv_name) < 0) {
log_error("Failed to create path for %s.", display_lvname(cachevol_lv));
return 0;
}
if (!(cn = find_config_tree_array(cmd, global_cache_repair_options_CFG, NULL))) {
log_error(INTERNAL_ERROR "Unable to find configuration for global/cache_repair_options");
return 0;
}
for (cv = cn->v; cv && args < MAX_PDATA_ARGS; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_error("Invalid string in config file: "
"global/cache_repair_options");
return 0;
}
argv[++args] = cv->v.str;
}
if (args >= MAX_PDATA_ARGS) {
log_error("Too many cache_repair_options set in lvm.conf.");
return 0;
}
argv[0] = cache_repair;
argv[++args] = "-i";
argv[++args] = cachevol_lv_path;
argv[++args] = "-o";
argv[++args] = meta_file;
argv[++args] = NULL;
if (!exec_cmd(cmd, (const char * const *)argv, &status, 1)) {
log_error("The cache_repair command failed (status %d).", status);
return 0;
}
log_print("cache_repair of metadata completed from %s to %s", display_lvname(cachevol_lv), meta_file);
return 1;
}
static int _run_cache_writeback_from_cachevol(struct cmd_context *cmd,
struct logical_volume *cache_lv,
struct logical_volume *cachevol_lv,
const char *meta_file,
uint64_t data_offset_bytes)
{
char orig_path[PATH_MAX];
char data_path[PATH_MAX];
char *orig_name;
char *data_name;
const char *dmdir = dm_dir();
const char *cache_writeback = "cache_writeback";
const char *argv[MAX_PDATA_ARGS + 7]; /* Max supported args */
char offset_str[16];
int args = 0;
int status;
if (!(orig_name = dm_build_dm_name(cmd->mem, cache_lv->vg->name, cache_lv->name, NULL))) {
log_error("Failed to create dm name for %s.", display_lvname(cache_lv));
}
if (!(data_name = dm_build_dm_name(cmd->mem, cachevol_lv->vg->name, cachevol_lv->name, NULL))) {
log_error("Failed to create dm name for %s.", display_lvname(cachevol_lv));
}
if (dm_snprintf(orig_path, sizeof(orig_path), "%s/%s", dmdir, orig_name) < 0) {
log_error("Failed to create path for %s.", display_lvname(cache_lv));
return 0;
}
/* _off suffix is temp hack to allow manual offset */
if (dm_snprintf(data_path, sizeof(data_path), "%s/%s_off", dmdir, data_name) < 0) {
log_error("Failed to create path for %s.", display_lvname(cachevol_lv));
return 0;
}
if (dm_snprintf(offset_str, sizeof(offset_str), "%llu",
(unsigned long long)data_offset_bytes) < 0)
return_0;
/* let the user set up an _off dm wrapper */
if (yes_no_prompt("Done creating wrapper %s with offset %u sectors? [y/n]: ",
data_path, (uint32_t)data_offset_bytes/512) == 'n')
return_0;
argv[0] = cache_writeback;
argv[++args] = "--metadata-device";
argv[++args] = meta_file;
argv[++args] = "--origin-device";
argv[++args] = orig_path;
argv[++args] = "--fast-device";
argv[++args] = data_path;
/*
argv[++args] = "--fast-device-offset";
argv[++args] = offset_str;
*/
argv[++args] = "--no-metadata-update";
argv[++args] = NULL;
if (!exec_cmd(cmd, (const char * const *)argv, &status, 1)) {
log_error("The cache_writeback command failed (status %d).", status);
return 0;
}
log_print("cache_writeback of data completed from %s to %s",
display_lvname(cachevol_lv), display_lvname(cache_lv));
return 1;
}
static int _lvconvert_cache_repair_cachevol(struct cmd_context *cmd,
struct logical_volume *cache_lv,
struct dm_list *pvh)
{
char filepath[PATH_MAX];
char zero_sector_buf[512];
struct volume_group *vg = cache_lv->vg;
struct logical_volume *cachevol_lv;
struct lv_segment *cache_seg, *cachevol_seg;
uint64_t metadata_len_sectors, data_offset_bytes;
uint64_t orig_cachevol_seglen;
const char *meta_file = NULL;
FILE *fp;
uint32_t prealloc_sectors_written = 0;
int cache_mode;
int ret = 0;
memset(zero_sector_buf, 0, 512);
if (!(cache_seg = first_seg(cache_lv)))
return_0;
if (!(cachevol_lv = cache_seg->pool_lv))
return_0;
if (!lv_is_cache_vol(cachevol_lv))
return_0;
if (cache_seg->metadata_start) {
/* metadata is always at 0, but check in case it changes in future */
log_error("Cannot handle non-zero metadata start locations.");
return 0;
}
if (lv_is_active(cache_lv)) {
log_error("LV %s must not be active.", display_lvname(cache_lv));
return 0;
}
if (lv_is_active(cachevol_lv)) {
log_error("LV %s must not be active.", display_lvname(cachevol_lv));
return 0;
}
if (vg_is_shared(vg)) {
/* cache LV using the cachevol cannot be active elsewhere */
if (!lockd_lv(cmd, cache_lv, "ex", 0))
return_0;
/* cachevol cannot be active elsewhere. */
if (!lockd_lv(cmd, cachevol_lv, "ex", 0))
return_0;
}
metadata_len_sectors = cache_seg->metadata_len;
cache_mode = cache_seg->cache_mode;
/* Disable syncing the cache during split */
if (cache_mode != CACHE_MODE_WRITETHROUGH)
cache_seg->cache_mode = CACHE_MODE_WRITETHROUGH;
if (!_lvconvert_split_and_keep_cachevol(cmd, cache_lv, cachevol_lv)) {
log_error("Failed to detach cachevol, not repairing.");
return_0;
}
/*
* Note: following the spilt, cache_lv is no longer technically a cache
* and cachevol_lv is no longer technically a cachevol. The variable
* names represent their previous types.
*/
if (cache_mode == CACHE_MODE_WRITETHROUGH) {
log_print("No repair or writeback required for writethrough cache mode.");
return 1;
}
/*
* Prevent the active LVs from being exposed and used outside of lvm.
*/
cachevol_lv->status |= LV_TEMPORARY;
cache_lv->status |= LV_TEMPORARY;
/*
* Temporarily change the cachevol LV size to be only the
* metadata size, so cache_repair will only look at the
* metadata portion.
* seg->len and seg->metadata_len units are sectors
*/
cachevol_seg = first_seg(cachevol_lv);
orig_cachevol_seglen = cachevol_seg->len;
cachevol_seg->len = metadata_len_sectors / vg->extent_size;
if (!activate_lv(cmd, cachevol_lv)) {
log_error("Failed to activate LV %s for cache repair and writeback.", display_lvname(cachevol_lv));
goto out;
}
if (!sync_local_dev_names(cmd)) {
log_error("Failed to sync local devices before cache_repair.");
goto out;
}
/*
* cache_repair wants the destination file to be preallocated.
* FIXME: what's the relationship between the metadata size and
* the file size?
*/
if (arg_is_set(cmd, file_long_ARG))
meta_file = arg_str_value(cmd, file_long_ARG, NULL);
else {
snprintf(filepath, PATH_MAX, "/tmp/lvconvert-cache-repair-metadata-%d", getpid());
meta_file = filepath;
}
log_print("Allocating %u MB for metadata in %s",
(uint32_t)(metadata_len_sectors * 512 / 1048576), meta_file);
if (!(fp = fopen(meta_file, "w+"))) {
log_error("Failed to open file for metadata %s", meta_file);
meta_file = NULL;
goto out;
}
while (prealloc_sectors_written < metadata_len_sectors) {
if (!fwrite(zero_sector_buf, 512, 1, fp)) {
fclose(fp);
log_error("Failed to allocate space for metadata in %s", meta_file);
goto out;
}
prealloc_sectors_written++;
}
fflush(fp);
fclose(fp);
if (!_run_cache_repair_from_cachevol(cmd, cachevol_lv, meta_file)) {
log_error("cache_repair failed, cache data not written back to original volume.");
goto out;
}
/*
* Deactivate cachevol LV, and reactivate with correct size.
* Activate cache LV.
*/
if (!deactivate_lv(cmd, cachevol_lv)) {
log_error("Failed to deactivate LV %s", cachevol_lv->name);
goto out;
}
cachevol_seg->len = orig_cachevol_seglen;
if (!activate_lv(cmd, cachevol_lv)) {
log_error("Failed to activate LV %s for cache repair and writeback.", display_lvname(cachevol_lv));
goto out;
}
if (!activate_lv(cmd, cache_lv)) {
log_error("Failed to activate LV %s for cache repair and writeback.", display_lvname(cache_lv));
goto out;
}
if (!sync_local_dev_names(cmd)) {
log_error("Failed to sync local devices before cache_writeback.");
goto out;
}
data_offset_bytes = metadata_len_sectors * 512;
if (!_run_cache_writeback_from_cachevol(cmd, cache_lv, cachevol_lv, meta_file, data_offset_bytes)) {
log_error("cache_writeback failed");
goto_out;
}
log_print("cachevol writeback complete, %s now unused.", cachevol_lv->name);
ret = 1;
out:
if (!deactivate_lv(cmd, cache_lv))
log_error("Failed to deactivate LV %s", cache_lv->name);
if (!deactivate_lv(cmd, cachevol_lv))
log_error("Failed to deactivate LV %s", cachevol_lv->name);
if (meta_file)
unlink(meta_file);
if (!ret)
log_error("Cache data on %s not written back to original LV %s.",
display_lvname(cachevol_lv), display_lvname(cache_lv));
return ret;
}
static int _lvconvert_cache_repair_cachepool(struct cmd_context *cmd,
static int _lvconvert_cache_repair(struct cmd_context *cmd,
struct logical_volume *cache_lv,
struct dm_list *pvh, int poolmetadataspare)
{
@@ -2831,6 +2502,11 @@ static int _lvconvert_cache_repair_cachepool(struct cmd_context *cmd,
struct logical_volume *pmslv;
struct logical_volume *mlv;
if (lv_is_cache(cache_lv) && lv_is_cache_vol(first_seg(cache_lv)->pool_lv)) {
log_error("Manual repair required.");
return 0;
}
pool_lv = lv_is_cache_pool(cache_lv) ? cache_lv : first_seg(cache_lv)->pool_lv;
mlv = first_seg(pool_lv)->metadata_lv;
@@ -4093,7 +3769,7 @@ static int _lvconvert_repair_pvs(struct cmd_context *cmd, struct logical_volume
return ret ? ECMD_PROCESSED : ECMD_FAILED;
}
static int _lvconvert_repair_cache_or_thin(struct cmd_context *cmd, struct logical_volume *lv,
static int _lvconvert_repair_cachepool_thinpool(struct cmd_context *cmd, struct logical_volume *lv,
struct processing_handle *handle)
{
int poolmetadataspare = arg_int_value(cmd, poolmetadataspare_ARG, DEFAULT_POOL_METADATA_SPARE);
@@ -4114,13 +3790,8 @@ static int _lvconvert_repair_cache_or_thin(struct cmd_context *cmd, struct logic
if (!_lvconvert_thin_pool_repair(cmd, lv, use_pvh, poolmetadataspare))
return_ECMD_FAILED;
} else /* cache */ {
if (lv_is_cache(lv) && lv_is_cache_vol(first_seg(lv)->pool_lv)) {
if (!_lvconvert_cache_repair_cachevol(cmd, lv, use_pvh))
return_ECMD_FAILED;
} else {
if (!_lvconvert_cache_repair_cachepool(cmd, lv, use_pvh, poolmetadataspare))
return_ECMD_FAILED;
}
if (!_lvconvert_cache_repair(cmd, lv, use_pvh, poolmetadataspare))
return_ECMD_FAILED;
}
return ECMD_PROCESSED;
@@ -4132,7 +3803,7 @@ static int _lvconvert_repair_single(struct cmd_context *cmd, struct logical_volu
if (lv_is_thin_pool(lv) ||
lv_is_cache(lv) ||
lv_is_cache_pool(lv))
return _lvconvert_repair_cache_or_thin(cmd, lv, handle);
return _lvconvert_repair_cachepool_thinpool(cmd, lv, handle);
if (lv_is_raid(lv) || lv_is_mirror(lv))
return _lvconvert_repair_pvs(cmd, lv, handle);
@@ -4143,6 +3814,12 @@ static int _lvconvert_repair_single(struct cmd_context *cmd, struct logical_volu
return ECMD_FAILED;
}
/*
* FIXME: add option --repair-pvs to call _lvconvert_repair_pvs() directly,
* and option --repair-thinpool to call _lvconvert_repair_thinpool().
* and option --repair-cache to call _lvconvert_repair_cache().
* and option --repair-cachepool to call _lvconvert_repair_cachepool().
*/
int lvconvert_repair_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
@@ -6053,6 +5730,75 @@ int lvconvert_to_cache_with_cachevol_cmd(struct cmd_context *cmd, int argc, char
return ret;
}
static int _lvconvert_integrity_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle)
{
struct volume_group *vg = lv->vg;
struct lv_segment *seg;
const char *arg = arg_str_value(cmd, integrity_ARG, NULL);
if (strcmp(arg, "none") && strcmp(arg, "n")) {
log_error("Integrity can only be removed from an existing LV (see --integrity none).");
return ECMD_FAILED;
}
if (!lv_is_integrity(lv)) {
log_error("LV does not have integrity.");
return ECMD_FAILED;
}
seg = first_seg(lv);
if (!seg->integrity_meta_dev) {
log_error("Internal integrity cannot be removed.");
return ECMD_FAILED;
}
/* TODO: lift this restriction */
if (lv_info(cmd, lv, 1, NULL, 0, 0)) {
log_error("LV must be inactive to remove integrity.");
return ECMD_FAILED;
}
if (!archive(vg))
return ECMD_FAILED;
if (!lv_remove_integrity(lv))
return ECMD_FAILED;
if (!vg_write(vg) || !vg_commit(vg))
return ECMD_FAILED;
backup(vg);
log_print_unless_silent("Logical volume %s has removed integrity.", display_lvname(lv));
return ECMD_PROCESSED;
}
int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
struct lvconvert_result lr = { 0 };
int ret;
if (!(handle = init_processing_handle(cmd, NULL))) {
log_error("Failed to initialize processing handle.");
return ECMD_FAILED;
}
handle->custom_handle = &lr;
cmd->cname->flags &= ~GET_VGNAME_FROM_OPTIONS;
ret = process_each_lv(cmd, cmd->position_argc, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, handle, NULL,
&_lvconvert_integrity_single);
destroy_processing_handle(cmd, handle);
return ret;
}
/*
* All lvconvert command defs have their own function,
* so the generic function name is unused.

View File

@@ -785,6 +785,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
mirror_default_cfg = (arg_uint_value(cmd, stripes_ARG, 1) > 1)
? global_raid10_segtype_default_CFG : global_mirror_segtype_default_CFG;
segtype_str = find_config_tree_str(cmd, mirror_default_cfg, NULL);
} else if (arg_is_set(cmd, integrity_ARG)) {
segtype_str = SEG_TYPE_NAME_INTEGRITY;
} else
segtype_str = SEG_TYPE_NAME_STRIPED;
@@ -846,6 +848,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
poolmetadataspare_ARG
#define RAID_ARGS \
integrity_ARG,\
integritysettings_ARG,\
maxrecoveryrate_ARG,\
minrecoveryrate_ARG,\
raidmaxrecoveryrate_ARG,\
@@ -1218,6 +1222,11 @@ static int _lvcreate_params(struct cmd_context *cmd,
}
}
if (seg_is_integrity(lp) || seg_is_raid(lp)) {
if (!get_integrity_options(cmd, &lp->integrity_arg, &lp->integrity_meta_name, &lp->integrity_settings))
return 0;
}
lcp->pv_count = argc;
lcp->pvs = argv;
@@ -1575,6 +1584,13 @@ static int _check_zero_parameters(struct cmd_context *cmd, struct lvcreate_param
if (seg_is_thin(lp))
return 1;
if (seg_is_integrity(lp)) {
if (lp->zero && !is_change_activating(lp->activate)) {
log_error("Zeroing integrity is not compatible with inactive creation (-an).");
return 0;
}
}
/* If there is some problem, buffer will not be empty */
if (dm_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
lp->origin_name ? "origin " : "",
@@ -1702,6 +1718,24 @@ static int _lvcreate_single(struct cmd_context *cmd, const char *vg_name,
lp->pool_name ? : "with generated name", lp->vg_name, lp->segtype->name);
}
/*
* Create an LV to hold integrity metadata when:
*
* . The user wants external (not internal) metadata. External
* is indicated by: no option set (external is default), or
* --integrity y|external.
*
* . The user did not specify an existing metadata LV with
* --integritymetadata, saved as lp->integrity_meta_name.
*/
if (seg_is_integrity(lp) && !lp->integrity_meta_name &&
(!lp->integrity_arg ||
!strcmp(lp->integrity_arg, "external") ||
!strcmp(lp->integrity_arg, "y"))) {
if (!lv_create_integrity_metadata(cmd, vg, lp))
goto_out;
}
if (vg->lock_type && !strcmp(vg->lock_type, "sanlock")) {
if (!handle_sanlock_lv(cmd, vg)) {
log_error("No space for sanlock lock, extend the internal lvmlock LV.");
@@ -1775,5 +1809,17 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv)
_destroy_lvcreate_params(&lp);
destroy_processing_handle(cmd, handle);
/*
* When set, the final LV should be zeroed (set by add_integrity
* to intialize integrity metadata/checksums).
*/
if (lp.zero_data_sectors) {
if (!lp.zero)
log_warn("WARNING: not zeroing integrity LV, read errors are possible.");
else
zero_lv_name(cmd, lp.vg_name, lp.lv_name, lp.zero_data_sectors);
}
return ret;
}

View File

@@ -149,6 +149,9 @@ static const struct command_function _command_functions[CMD_COUNT] = {
{ lvconvert_to_vdopool_CMD, lvconvert_to_vdopool_cmd },
{ lvconvert_to_vdopool_param_CMD, lvconvert_to_vdopool_param_cmd },
/* lvconvert for integrity */
{ lvconvert_integrity_CMD, lvconvert_integrity_cmd },
{ pvscan_display_CMD, pvscan_display_cmd },
{ pvscan_cache_CMD, pvscan_cache_cmd },
};

View File

@@ -359,7 +359,10 @@ static int _dump_all_text(struct cmd_context *cmd, const char *tofile, struct de
continue;
}
sscanf(line, "seqno = %u", &seqno);
if (sscanf(line, "seqno = %u", &seqno) != 1) {
log_warn("WARNING: Failed to parse seqno.");
seqno = 0; /* Skip? */
}
/*
* The first three lines look like metadata with
@@ -417,11 +420,11 @@ static int _dump_all_text(struct cmd_context *cmd, const char *tofile, struct de
memset(line, 0, sizeof(line));
_copy_line(str1, line, &len);
log_print("%s", line);
}
if ((str2 = strstr(str1, "creation_time = "))) {
memset(line, 0, sizeof(line));
_copy_line(str2, line, &len);
log_print("%s\n", line);
if ((str2 = strstr(str1, "creation_time = "))) {
memset(line, 0, sizeof(line));
_copy_line(str2, line, &len);
log_print("%s\n", line);
}
}
}
@@ -652,27 +655,31 @@ static int _dump_meta_area(struct device *dev, const char *tofile,
if (!tofile)
return_0;
if (!(meta_buf = malloc(mda_size)))
if (!(meta_buf = zalloc(mda_size)))
return_0;
memset(meta_buf, 0, mda_size);
if (!dev_read_bytes(dev, mda_offset, mda_size, meta_buf)) {
log_print("CHECK: failed to read metadata area at offset %llu size %llu",
(unsigned long long)mda_offset, (unsigned long long)mda_size);
free(meta_buf);
return 0;
}
if (!(fp = fopen(tofile, "wx"))) {
log_error("Failed to create file %s", tofile);
log_error("Failed to create file %s.", tofile);
free(meta_buf);
return 0;
}
fwrite(meta_buf, mda_size - 512, 1, fp);
if (fwrite(meta_buf, mda_size - 512, 1, fp) < (mda_size - 512))
log_warn("WARNING: Failed to write " FMTu64 " bytes to file %s.", mda_size - 512, tofile);
free(meta_buf);
if (fflush(fp))
stack;
if (fclose(fp))
stack;
return 1;
}
@@ -705,12 +712,11 @@ static int _dump_current_text(struct device *dev,
int ri = rlocn_index; /* 0 or 1 */
int bad = 0;
if (!(meta_buf = malloc(meta_size))) {
if (!(meta_buf = zalloc(meta_size))) {
log_print("CHECK: mda_header_%d.raw_locn[%d] no mem for metadata text size %llu", mn, ri,
(unsigned long long)meta_size);
return 0;
}
memset(meta_buf, 0, meta_size);
/*
* Read the metadata text specified by the raw_locn so we can
@@ -721,7 +727,7 @@ static int _dump_current_text(struct device *dev,
* mda_offset + meta_offset.
*/
if (meta_offset + meta_size > mda_size) {
/* text metadata wraps to start of text metadata area */
/* text metadata wraps to start of text metadata area */
uint32_t wrap = (uint32_t) ((meta_offset + meta_size) - mda_size);
off_t offset_a = mda_offset + meta_offset;
uint32_t size_a = meta_size - wrap;
@@ -732,6 +738,7 @@ static int _dump_current_text(struct device *dev,
log_print("CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu part_a %llu %llu", mn, ri,
(unsigned long long)meta_offset, (unsigned long long)meta_size,
(unsigned long long)offset_a, (unsigned long long)size_a);
free(meta_buf);
return 0;
}
@@ -739,12 +746,14 @@ static int _dump_current_text(struct device *dev,
log_print("CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu part_b %llu %llu", mn, ri,
(unsigned long long)meta_offset, (unsigned long long)meta_size,
(unsigned long long)offset_b, (unsigned long long)size_b);
free(meta_buf);
return 0;
}
} else {
if (!dev_read_bytes(dev, mda_offset + meta_offset, meta_size, meta_buf)) {
log_print("CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu", mn, ri,
(unsigned long long)meta_offset, (unsigned long long)meta_size);
free(meta_buf);
return 0;
}
}
@@ -802,9 +811,9 @@ static int _dump_current_text(struct device *dev,
}
out:
if (bad)
return 0;
return 1;
free(meta_buf);
return (!bad) ? 1 : 0;
}
static int _dump_label_and_pv_header(struct cmd_context *cmd, int print_fields,
@@ -848,6 +857,7 @@ static int _dump_label_and_pv_header(struct cmd_context *cmd, int print_fields,
if (!dev_read_bytes(dev, lh_offset, 512, buf)) {
log_print("CHECK: failed to read label_header at %llu",
(unsigned long long)lh_offset);
free(buf);
return 0;
}
@@ -1042,9 +1052,9 @@ static int _dump_label_and_pv_header(struct cmd_context *cmd, int print_fields,
(unsigned long long)xlate64(dlocn->size));
}
if (bad)
return 0;
return 1;
free(buf);
return (!bad) ? 1 : 0;
}
/*
@@ -1097,6 +1107,7 @@ static int _dump_mda_header(struct cmd_context *cmd,
if (!dev_read_bytes(dev, mda_offset, 512, buf)) {
log_print("CHECK: failed to read mda_header at %llu", (unsigned long long)mda_offset);
free(buf);
return 0;
}
@@ -1187,11 +1198,9 @@ static int _dump_mda_header(struct cmd_context *cmd,
/* Should we also check text metadata if it exists in rlocn1? */
out:
if (buf)
free(buf);
if (bad)
return 0;
return 1;
free(buf);
return (!bad) ? 1 : 0;
}
static int _dump_headers(struct cmd_context *cmd,
@@ -1457,7 +1466,8 @@ static int _dump_search(struct cmd_context *cmd,
uint64_t dev_bytes;
uint64_t extra_bytes;
dev_get_size(dev, &dev_sectors);
if (!dev_get_size(dev, &dev_sectors))
return_ECMD_FAILED;
dev_bytes = dev_sectors * 512;
extra_bytes = dev_bytes % ONE_MB_IN_BYTES;
@@ -1484,9 +1494,8 @@ static int _dump_search(struct cmd_context *cmd,
log_print("Searching for metadata in mda%d at offset %llu size %llu", mda_num,
(unsigned long long)mda_offset, (unsigned long long)mda_size);
if (!(buf = malloc(mda_size)))
if (!(buf = zalloc(mda_size)))
return ECMD_FAILED;
memset(buf, 0, mda_size);
if (!dev_read_bytes(dev, mda_offset, mda_size, buf)) {
log_print("CHECK: failed to read metadata area at offset %llu size %llu",

View File

@@ -420,7 +420,7 @@ check_duplicate:
log_error("pvscan[%d] PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
getpid(), dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
if (file_vgname[0] && strcmp(file_vgname, vgname))
if (file_vgname[0] && vgname && strcmp(file_vgname, vgname))
log_error("pvscan[%d] PV %s has unexpected VG %s vs %s.",
getpid(), dev_name(dev), vgname, file_vgname);

View File

@@ -1414,6 +1414,174 @@ out:
return ok;
}
static int _get_one_integrity_setting(struct cmd_context *cmd, struct integrity_settings *settings,
char *key, char *val)
{
if (!strncmp(key, "mode", strlen("mode"))) {
if (*val == 'D')
settings->mode[0] = 'D';
else if (*val == 'J')
settings->mode[0] = 'J';
else if (*val == 'B')
settings->mode[0] = 'B';
else if (*val == 'R')
settings->mode[0] = 'R';
else
goto_bad;
/* lvm assigns a default if the user doesn't. */
return 1;
}
if (!strncmp(key, "tag_size", strlen("tag_size"))) {
if (sscanf(val, "%u", &settings->tag_size) != 1)
goto_bad;
/* lvm assigns a default if the user doesn't. */
return 1;
}
if (!strncmp(key, "internal_hash", strlen("internal_hash"))) {
if (!(settings->internal_hash = strdup(val)))
goto_bad;
/* lvm assigns a default if the user doesn't. */
return 1;
}
/*
* For the following settings, lvm does not set a default value if the
* user does not specify their own value, and lets dm-integrity use its
* own default.
*/
if (!strncmp(key, "journal_sectors", strlen("journal_sectors"))) {
if (sscanf(val, "%u", &settings->journal_sectors) != 1)
goto_bad;
settings->journal_sectors_set = 1;
return 1;
}
if (!strncmp(key, "interleave_sectors", strlen("interleave_sectors"))) {
if (sscanf(val, "%u", &settings->interleave_sectors) != 1)
goto_bad;
settings->interleave_sectors_set = 1;
return 1;
}
if (!strncmp(key, "buffer_sectors", strlen("buffer_sectors"))) {
if (sscanf(val, "%u", &settings->buffer_sectors) != 1)
goto_bad;
settings->buffer_sectors_set = 1;
return 1;
}
if (!strncmp(key, "journal_watermark", strlen("journal_watermark"))) {
if (sscanf(val, "%u", &settings->journal_watermark) != 1)
goto_bad;
settings->journal_watermark_set = 1;
return 1;
}
if (!strncmp(key, "commit_time", strlen("commit_time"))) {
if (sscanf(val, "%u", &settings->commit_time) != 1)
goto_bad;
settings->commit_time_set = 1;
return 1;
}
if (!strncmp(key, "block_size", strlen("block_size"))) {
if (sscanf(val, "%u", &settings->block_size) != 1)
goto_bad;
settings->block_size_set = 1;
return 1;
}
if (!strncmp(key, "bitmap_flush_interval", strlen("bitmap_flush_interval"))) {
if (sscanf(val, "%u", &settings->bitmap_flush_interval) != 1)
goto_bad;
settings->bitmap_flush_interval_set = 1;
return 1;
}
if (!strncmp(key, "sectors_per_bit", strlen("sectors_per_bit"))) {
if (sscanf(val, "%llu", (unsigned long long *)&settings->sectors_per_bit) != 1)
goto_bad;
settings->sectors_per_bit_set = 1;
return 1;
}
log_error("Unknown setting: %s", key);
return 0;
bad:
log_error("Invalid setting: %s", key);
return 0;
}
static int _get_integrity_settings(struct cmd_context *cmd, struct integrity_settings *settings)
{
struct arg_value_group_list *group;
const char *str;
char key[64];
char val[64];
int num;
int pos;
/*
* "grouped" means that multiple --integritysettings options can be used.
* Each option is also allowed to contain multiple key = val pairs.
*/
dm_list_iterate_items(group, &cmd->arg_value_groups) {
if (!grouped_arg_is_set(group->arg_values, integritysettings_ARG))
continue;
if (!(str = grouped_arg_str_value(group->arg_values, integritysettings_ARG, NULL)))
break;
pos = 0;
while (pos < strlen(str)) {
/* scan for "key1=val1 key2 = val2 key3= val3" */
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
if (sscanf(str + pos, " %63[^=]=%63s %n", key, val, &num) != 2) {
log_error("Invalid setting at: %s", str+pos);
return 0;
}
pos += num;
if (!_get_one_integrity_setting(cmd, settings, key, val))
return_0;
}
}
return 1;
}
int get_integrity_options(struct cmd_context *cmd, const char **arg, const char **meta_name,
struct integrity_settings *set)
{
*arg = NULL;
*meta_name = NULL;
memset(set, 0, sizeof(struct integrity_settings));
if (arg_is_set(cmd, integrity_ARG))
*arg = arg_str_value(cmd, integrity_ARG, NULL);
if (arg_is_set(cmd, integritymetadata_ARG))
*meta_name = arg_str_value(cmd, integritymetadata_ARG, NULL);
if (arg_is_set(cmd, integritysettings_ARG)) {
if (!_get_integrity_settings(cmd, set))
return_0;
}
return 1;
}
/* FIXME move to lib */
static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag)
{
@@ -2579,6 +2747,8 @@ static int _lv_is_type(struct cmd_context *cmd, struct logical_volume *lv, int l
return seg_is_raid10(seg);
case writecache_LVT:
return seg_is_writecache(seg);
case integrity_LVT:
return seg_is_integrity(seg);
case error_LVT:
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR);
case zero_LVT:
@@ -2637,6 +2807,8 @@ int get_lvt_enum(struct logical_volume *lv)
return raid10_LVT;
if (seg_is_writecache(seg))
return writecache_LVT;
if (seg_is_integrity(seg))
return integrity_LVT;
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR))
return error_LVT;
@@ -4284,7 +4456,8 @@ static int _process_pvs_in_vg(struct cmd_context *cmd,
/*
* When processing only specific PVs, we can quit once they've all been found.
*/
if (!process_all_pvs && dm_list_empty(arg_tags) && dm_list_empty(arg_devices))
if (!process_all_pvs && dm_list_empty(arg_tags) &&
(!arg_devices || dm_list_empty(arg_devices)))
break;
log_set_report_object_name_and_id(NULL, NULL);
}

View File

@@ -233,6 +233,9 @@ struct lv_prop *get_lv_prop(int lvp_enum);
struct lv_type *get_lv_type(int lvt_enum);
struct command *get_command(int cmd_enum);
int get_integrity_options(struct cmd_context *cmd, const char **arg, const char **meta_name,
struct integrity_settings *set);
int lvchange_properties_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_activate_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_refresh_cmd(struct cmd_context *cmd, int argc, char **argv);
@@ -272,6 +275,8 @@ int lvconvert_merge_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_param_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv);
int pvscan_display_cmd(struct cmd_context *cmd, int argc, char **argv);
int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv);