mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
337 lines
9.5 KiB
C
337 lines
9.5 KiB
C
/*
|
|
* Copyright (C) 2011 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "lib.h"
|
|
#include "toolcontext.h"
|
|
#include "metadata.h"
|
|
#include "segtype.h"
|
|
#include "text_export.h"
|
|
#include "text_import.h"
|
|
#include "config.h"
|
|
#include "activate.h"
|
|
#include "str_list.h"
|
|
|
|
#ifdef DMEVENTD
|
|
# include "sharedlib.h"
|
|
# include "libdevmapper-event.h"
|
|
#endif
|
|
|
|
/* Dm kernel module name for thin provisiong */
|
|
#define THIN_MODULE "thin-pool"
|
|
|
|
/*
|
|
* Macro used as return argument - returns 0.
|
|
* return is left to be written in the function for better readability.
|
|
*/
|
|
#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 const char *_thin_pool_name(const struct lv_segment *seg)
|
|
{
|
|
return seg->segtype->name;
|
|
}
|
|
|
|
static int _thin_pool_text_import(struct lv_segment *seg,
|
|
const struct dm_config_node *sn,
|
|
struct dm_hash_table *pv_hash __attribute__((unused)))
|
|
{
|
|
const char *lv_name;
|
|
struct logical_volume *pool_data_lv, *pool_metadata_lv;
|
|
|
|
if (!dm_config_get_str(sn, "pool", &lv_name))
|
|
return SEG_LOG_ERROR("Pool must be a string in");
|
|
|
|
if (!(pool_data_lv = find_lv(seg->lv->vg, lv_name)))
|
|
return SEG_LOG_ERROR("Unknown pool %s in", lv_name);
|
|
|
|
if (!dm_config_get_str(sn, "metadata", &lv_name))
|
|
return SEG_LOG_ERROR("Metadata must be a string in");
|
|
|
|
if (!(pool_metadata_lv = find_lv(seg->lv->vg, lv_name)))
|
|
return SEG_LOG_ERROR("Unknown metadata %s in", lv_name);
|
|
|
|
if (!attach_pool_data_lv(seg, pool_data_lv))
|
|
return_0;
|
|
|
|
if (!attach_pool_metadata_lv(seg, pool_metadata_lv))
|
|
return_0;
|
|
|
|
if (!dm_config_get_uint64(sn, "transaction_id", &seg->transaction_id))
|
|
return SEG_LOG_ERROR("Could not read transaction_id for");
|
|
|
|
if (!dm_config_get_uint64(sn, "low_water_mark", &seg->low_water_mark))
|
|
return SEG_LOG_ERROR("Could not read low_water_mark");
|
|
|
|
if (!dm_config_get_uint32(sn, "data_block_size", &seg->data_block_size))
|
|
return SEG_LOG_ERROR("Could not read data_block_size");
|
|
|
|
if ((seg->data_block_size < DM_THIN_MIN_DATA_SIZE) ||
|
|
(seg->data_block_size > DM_THIN_MAX_DATA_SIZE))
|
|
return SEG_LOG_ERROR("Unsupported value %u for data_block_size",
|
|
seg->device_id);
|
|
|
|
if (dm_config_has_node(sn, "zero_new_blocks") &&
|
|
!dm_config_get_uint32(sn, "zero_new_blocks", &seg->zero_new_blocks))
|
|
return SEG_LOG_ERROR("Could not read zero_new_blocks for");
|
|
|
|
seg->lv->status |= THIN_POOL;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _thin_pool_text_import_area_count(const struct dm_config_node *sn,
|
|
uint32_t *area_count)
|
|
{
|
|
*area_count = 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _thin_pool_text_export(const struct lv_segment *seg, struct formatter *f)
|
|
{
|
|
outf(f, "pool = \"%s\"", seg_lv(seg, 0)->name);
|
|
outf(f, "metadata = \"%s\"", seg->pool_metadata_lv->name);
|
|
outf(f, "transaction_id = %" PRIu64, seg->transaction_id);
|
|
outf(f, "low_water_mark = %" PRIu64, seg->low_water_mark);
|
|
outf(f, "data_block_size = %d", seg->data_block_size);
|
|
if (seg->zero_new_blocks)
|
|
outf(f, "zero_new_blocks = 1");
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef DEVMAPPER_SUPPORT
|
|
static int _thin_pool_add_target_line(struct dev_manager *dm,
|
|
struct dm_pool *mem __attribute__((unused)),
|
|
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 *metadata_dlid, *pool_dlid;
|
|
|
|
if (!(metadata_dlid = build_dm_uuid(mem, seg->pool_metadata_lv->lvid.s, NULL))) {
|
|
log_error("Failed to build uuid for metadata LV %s.", seg->pool_metadata_lv->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!(pool_dlid = build_dm_uuid(mem, seg_lv(seg, 0)->lvid.s, NULL))) {
|
|
log_error("Failed to build uuid for pool LV %s.", seg_lv(seg, 0)->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!dm_tree_node_add_thin_pool_target(node, len, 0, metadata_dlid, pool_dlid,
|
|
seg->data_block_size, seg->low_water_mark,
|
|
seg->zero_new_blocks ? 0 : 1))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static const char *_thin_name(const struct lv_segment *seg)
|
|
{
|
|
return seg->segtype->name;
|
|
}
|
|
|
|
static int _thin_text_import(struct lv_segment *seg,
|
|
const struct dm_config_node *sn,
|
|
struct dm_hash_table *pv_hash __attribute__((unused)))
|
|
{
|
|
const char *lv_name;
|
|
struct logical_volume *pool_lv;
|
|
|
|
if (!dm_config_get_str(sn, "thin_pool", &lv_name))
|
|
return SEG_LOG_ERROR("Thin pool must be a string in");
|
|
|
|
if (!(pool_lv = find_lv(seg->lv->vg, lv_name)))
|
|
return SEG_LOG_ERROR("Unknown thin pool %s in", lv_name);
|
|
|
|
if (!attach_pool_lv(seg, pool_lv))
|
|
return_0;
|
|
|
|
if (dm_config_has_node(sn, "origin")) {
|
|
if (!dm_config_get_str(sn, "origin", &lv_name))
|
|
return SEG_LOG_ERROR("Origin must be a string in");
|
|
|
|
if (!(seg->origin = find_lv(seg->lv->vg, lv_name)))
|
|
return SEG_LOG_ERROR("Unknown origin %s in", lv_name);
|
|
}
|
|
|
|
if (!dm_config_get_uint32(sn, "device_id", &seg->device_id))
|
|
return SEG_LOG_ERROR("Could not read device_id for");
|
|
|
|
if (seg->device_id > DM_THIN_MAX_DEVICE_ID)
|
|
return SEG_LOG_ERROR("Unsupported value %u for device_id",
|
|
seg->device_id);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _thin_text_export(const struct lv_segment *seg, struct formatter *f)
|
|
{
|
|
outf(f, "thin_pool = \"%s\"", seg->pool_lv->name);
|
|
outf(f, "device_id = %d", seg->device_id);
|
|
|
|
if (seg->origin)
|
|
outf(f, "origin = \"%s\"", seg->origin->name);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef DEVMAPPER_SUPPORT
|
|
static int _thin_add_target_line(struct dev_manager *dm,
|
|
struct dm_pool *mem __attribute__((unused)),
|
|
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 *thin_pool_dlid;
|
|
|
|
if (!(thin_pool_dlid = build_dm_uuid(mem, seg->pool_lv->lvid.s, NULL))) {
|
|
log_error("Failed to build uuid for thin pool LV %s.", seg->pool_lv->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!dm_tree_node_add_thin_target(node, len, thin_pool_dlid, seg->device_id))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _thin_target_percent(void **target_state __attribute__((unused)),
|
|
percent_t *percent,
|
|
struct dm_pool *mem __attribute__((unused)),
|
|
struct cmd_context *cmd __attribute__((unused)),
|
|
struct lv_segment *seg __attribute__((unused)),
|
|
char *params, uint64_t *total_numerator,
|
|
uint64_t *total_denominator)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int _thin_target_present(struct cmd_context *cmd,
|
|
const struct lv_segment *seg,
|
|
unsigned *attributes __attribute__((unused)))
|
|
{
|
|
static int _checked = 0;
|
|
static int _present = 0;
|
|
|
|
if (!_checked) {
|
|
_present = target_present(cmd, THIN_MODULE, 1);
|
|
_checked = 1;
|
|
}
|
|
|
|
return _present;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int _thin_modules_needed(struct dm_pool *mem,
|
|
const struct lv_segment *seg __attribute__((unused)),
|
|
struct dm_list *modules)
|
|
{
|
|
if (!str_list_add(mem, modules, THIN_MODULE)) {
|
|
log_error("thin string list allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void _thin_destroy(struct segment_type *segtype)
|
|
{
|
|
dm_free(segtype);
|
|
}
|
|
|
|
static struct segtype_handler _thin_pool_ops = {
|
|
.name = _thin_pool_name,
|
|
.text_import = _thin_pool_text_import,
|
|
.text_import_area_count = _thin_pool_text_import_area_count,
|
|
.text_export = _thin_pool_text_export,
|
|
#ifdef DEVMAPPER_SUPPORT
|
|
.add_target_line = _thin_pool_add_target_line,
|
|
.target_present = _thin_target_present,
|
|
#endif
|
|
.modules_needed = _thin_modules_needed,
|
|
.destroy = _thin_destroy,
|
|
};
|
|
|
|
static struct segtype_handler _thin_ops = {
|
|
.name = _thin_name,
|
|
.text_import = _thin_text_import,
|
|
.text_export = _thin_text_export,
|
|
#ifdef DEVMAPPER_SUPPORT
|
|
.add_target_line = _thin_add_target_line,
|
|
.target_percent = _thin_target_percent,
|
|
.target_present = _thin_target_present,
|
|
#endif
|
|
.modules_needed = _thin_modules_needed,
|
|
.destroy = _thin_destroy,
|
|
};
|
|
|
|
#ifdef THIN_INTERNAL
|
|
int init_thin_segtypes(struct cmd_context *cmd, struct segtype_library *seglib)
|
|
#else /* Shared */
|
|
int init_multiple_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
|
|
int init_multiple_segtypes(struct cmd_context *cmd, struct segtype_library *seglib)
|
|
#endif
|
|
{
|
|
static const struct {
|
|
struct segtype_handler *ops;
|
|
const char name[16];
|
|
uint32_t flags;
|
|
} reg_segtypes[] = {
|
|
{ &_thin_pool_ops, "thin_pool", SEG_THIN_POOL },
|
|
/* FIXME Maybe use SEG_THIN_VOLUME instead of SEG_VIRTUAL */
|
|
{ &_thin_ops, "thin", SEG_THIN_VOLUME | SEG_VIRTUAL }
|
|
};
|
|
|
|
struct segment_type *segtype;
|
|
unsigned i;
|
|
|
|
for (i = 0; i < sizeof(reg_segtypes)/sizeof(reg_segtypes[0]); ++i) {
|
|
segtype = dm_zalloc(sizeof(*segtype));
|
|
|
|
if (!segtype) {
|
|
log_error("Failed to allocate memory for %s segtype",
|
|
reg_segtypes[i].name);
|
|
return 0;
|
|
}
|
|
|
|
segtype->ops = reg_segtypes[i].ops;
|
|
segtype->name = reg_segtypes[i].name;
|
|
segtype->flags = reg_segtypes[i].flags;
|
|
|
|
#ifdef DEVMAPPER_SUPPORT
|
|
# ifdef DMEVENTD
|
|
// FIXME if (_get_thin_dso_path(cmd))
|
|
// FIXME segtype->flags |= SEG_MONITORED;
|
|
# endif /* DMEVENTD */
|
|
#endif
|
|
if (!lvm_register_segtype(seglib, segtype))
|
|
return_0;
|
|
|
|
log_very_verbose("Initialised segtype: %s", segtype->name);
|
|
}
|
|
|
|
return 1;
|
|
}
|