diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index fe325fac9..e6800cf7e 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -251,6 +251,7 @@ struct lv_segment *alloc_lv_segment(struct dm_pool *mem, seg->extents_copied = extents_copied; seg->pvmove_source_seg = pvmove_source_seg; dm_list_init(&seg->tags); + dm_list_init(&seg->thin_messages); if (thin_pool_lv && !attach_pool_lv(seg, thin_pool_lv)) return_NULL; @@ -4173,9 +4174,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, struct l return NULL; } - if (!lv_send_message(pool_lv, "create_thin %u", first_seg(lv)->device_id)) + if (!attach_pool_message(first_seg(pool_lv), + DM_THIN_MESSAGE_CREATE_THIN, lv, 0, 0)) return_NULL; - /* * FIXME: Skipping deactivate_lv(pool_lv) as it is going to be needed anyway * but revert_new_lv should revert to deactivated state. @@ -4238,6 +4239,15 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, struct l if (!set_lv(cmd, first_seg(lv)->pool_metadata_lv, UINT64_C(0), 0)) log_error("Aborting. Failed to wipe pool metadata %s.", lv->name); + } else if (seg_is_thin_volume(lp)) { + /* since we've got here, we may drop any queued thin messages */ + if (!detach_pool_messages(first_seg(first_seg(lv)->pool_lv))) + goto deactivate_and_revert_new_lv; + + if (!vg_write(vg) || !vg_commit(vg)) + goto deactivate_and_revert_new_lv; + + backup(vg); } if (lp->snapshot) { diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 74c6d2062..baee9ee9e 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -256,6 +256,15 @@ struct lv_segment_area { } u; }; +struct lv_thin_message { + struct dm_list list; /* Chained list of messages */ + dm_thin_message_t type; /* Use dm thin message datatype */ + union { + struct logical_volume *lv; /* For: create_thin, create_snap, trim */ + uint32_t delete_id; /* For delete, needs device_id */ + } u; +}; + struct segment_type; /* List with vg_name, vgid and flags */ @@ -338,6 +347,7 @@ struct lv_segment { uint64_t low_water_mark; /* For thin_pool */ uint32_t data_block_size; /* For thin_pool, 128..2097152 */ unsigned zero_new_blocks; /* For thin_pool */ + struct dm_list thin_messages; /* For thin_pool */ struct logical_volume *pool_lv; /* For thin */ uint32_t device_id; /* For thin, 24bit */ diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index d1b1b1f33..b71be4cee 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -454,6 +454,10 @@ int attach_pool_data_lv(struct lv_segment *seg, struct logical_volume *pool_data_lv); int attach_pool_lv(struct lv_segment *seg, struct logical_volume *pool_lv); int detach_pool_lv(struct lv_segment *seg); +int attach_pool_message(struct lv_segment *seg, dm_thin_message_t type, + struct logical_volume *lv, uint32_t device_id, + int read_only); +int detach_pool_messages(struct lv_segment *seg); /* * Begin skeleton for external LVM library diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c index 1afc2f37a..411850ee4 100644 --- a/lib/metadata/thin_manip.c +++ b/lib/metadata/thin_manip.c @@ -58,9 +58,75 @@ int detach_pool_lv(struct lv_segment *seg) return 0; } + if (!attach_pool_message(first_seg(seg->pool_lv), + DM_THIN_MESSAGE_DELETE, + NULL, seg->device_id, 0)) + return_0; + return remove_seg_from_segs_using_this_lv(seg->pool_lv, seg); } +int attach_pool_message(struct lv_segment *seg, dm_thin_message_t type, + struct logical_volume *lv, uint32_t device_id, + int read_only) +{ + struct lv_thin_message *tmsg; + + if (!lv_is_thin_pool(seg->lv)) { + log_error(INTERNAL_ERROR "LV %s is not a thin pool.", + seg->lv->name); + return 0; + } + + if (!(tmsg = dm_pool_alloc(seg->lv->vg->vgmem, sizeof(*tmsg)))) { + log_error("Failed to allocate memory for message."); + return 0; + } + + switch (type) { + case DM_THIN_MESSAGE_CREATE_SNAP: + case DM_THIN_MESSAGE_CREATE_THIN: + case DM_THIN_MESSAGE_TRIM: + tmsg->u.lv = lv; + break; + case DM_THIN_MESSAGE_DELETE: + tmsg->u.delete_id = device_id; + break; + default: + log_error(INTERNAL_ERROR "Unsupported message type %d", type); + return 0; + } + + tmsg->type = type; + + /* If the 1st message is add in non-read-only mode, modify transaction_id */ + if (!read_only && dm_list_empty(&seg->thin_messages)) + seg->transaction_id++; + + dm_list_add(&seg->thin_messages, &tmsg->list); + + log_debug("Added %s message", + (type == DM_THIN_MESSAGE_CREATE_SNAP || + type == DM_THIN_MESSAGE_CREATE_THIN) ? "create" : + (type == DM_THIN_MESSAGE_TRIM) ? "trim" : + (type == DM_THIN_MESSAGE_DELETE) ? "delete" : "unknown"); + + return 1; +} + +int detach_pool_messages(struct lv_segment *seg) +{ + if (!lv_is_thin_pool(seg->lv)) { + log_error(INTERNAL_ERROR "LV %s is not a thin pool.", + seg->lv->name); + return 0; + } + + dm_list_init(&seg->thin_messages); + + return 1; +} + struct lv_segment *find_pool_seg(const struct lv_segment *seg) { struct lv_segment *pool_seg; diff --git a/lib/thin/thin.c b/lib/thin/thin.c index 102fc8812..cfb627da2 100644 --- a/lib/thin/thin.c +++ b/lib/thin/thin.c @@ -43,6 +43,48 @@ static const char *_thin_pool_name(const struct lv_segment *seg) return seg->segtype->name; } +static int _thin_pool_add_message(struct lv_segment *seg, + const char *key, + const struct dm_config_node *sn) +{ + const char *lv_name = NULL; + struct logical_volume *lv = NULL; + uint32_t device_id = 0; + dm_thin_message_t type; + + /* Message must have only one from: create, trim, delete */ + if (dm_config_get_str(sn, "create", &lv_name)) { + if (!(lv = find_lv(seg->lv->vg, lv_name))) + return SEG_LOG_ERROR("Unknown LV %s for create message in", + lv_name); + /* FIXME: switch to _SNAP later, if the created LV has an origin */ + type = DM_THIN_MESSAGE_CREATE_THIN; + } + + if (dm_config_get_str(sn, "trim", &lv_name)) { + if (lv) + return SEG_LOG_ERROR("Unsupported message format in"); + if (!(lv = find_lv(seg->lv->vg, lv_name))) + return SEG_LOG_ERROR("Unknown LV %s for trim message in", + lv_name); + type = DM_THIN_MESSAGE_TRIM; + } + + if (!dm_config_get_uint32(sn, "delete", &device_id)) { + if (!lv) + return SEG_LOG_ERROR("Unknown message in"); + } else { + if (lv) + return SEG_LOG_ERROR("Unsupported message format in"); + type = DM_THIN_MESSAGE_DELETE; + } + + if (!attach_pool_message(seg, type, lv, device_id, 1)) + return_0; + + return 1; +} + static int _thin_pool_text_import(struct lv_segment *seg, const struct dm_config_node *sn, struct dm_hash_table *pv_hash __attribute__((unused))) @@ -88,6 +130,11 @@ static int _thin_pool_text_import(struct lv_segment *seg, seg->lv->status |= THIN_POOL; + /* Read messages */ + for (; sn; sn = sn->sib) + if (!(sn->v) && !_thin_pool_add_message(seg, sn->key, sn->child)) + return_0; + return 1; } @@ -101,14 +148,61 @@ static int _thin_pool_text_import_area_count(const struct dm_config_node *sn, static int _thin_pool_text_export(const struct lv_segment *seg, struct formatter *f) { + unsigned cnt = 0; + struct lv_thin_message *tmsg; + 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"); + dm_list_iterate_items(tmsg, &seg->thin_messages) { + /* Extra validation */ + switch (tmsg->type) { + case DM_THIN_MESSAGE_CREATE_SNAP: + case DM_THIN_MESSAGE_CREATE_THIN: + case DM_THIN_MESSAGE_TRIM: + if (!lv_is_thin_volume(tmsg->u.lv)) { + log_error(INTERNAL_ERROR + "LV %s is not a thin volume.", + tmsg->u.lv->name); + return 0; + } + break; + default: + break; + } + + if (!cnt) + outnl(f); + + outf(f, "message%d {", ++cnt); + out_inc_indent(f); + + switch (tmsg->type) { + case DM_THIN_MESSAGE_CREATE_SNAP: + case DM_THIN_MESSAGE_CREATE_THIN: + outf(f, "create = \"%s\"", tmsg->u.lv->name); + break; + case DM_THIN_MESSAGE_TRIM: + outf(f, "trim = \"%s\"", tmsg->u.lv->name); + break; + case DM_THIN_MESSAGE_DELETE: + outf(f, "delete = %d", tmsg->u.delete_id); + break; + default: + log_error(INTERNAL_ERROR "Passed unsupported message."); + return 0; + } + + out_dec_indent(f); + outf(f, "}"); + } + return 1; } @@ -123,6 +217,8 @@ static int _thin_pool_add_target_line(struct dev_manager *dm, uint32_t *pvmove_mirror_count __attribute__((unused))) { char *metadata_dlid, *pool_dlid; + struct lv_thin_message *lmsg; + struct dm_thin_message dmsg; 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); @@ -134,11 +230,55 @@ static int _thin_pool_add_target_line(struct dev_manager *dm, return 0; } - if (!dm_tree_node_add_thin_pool_target(node, len, 0, metadata_dlid, pool_dlid, + if (!dm_tree_node_add_thin_pool_target(node, len, seg->transaction_id, + metadata_dlid, pool_dlid, seg->data_block_size, seg->low_water_mark, seg->zero_new_blocks ? 0 : 1)) return_0; + if (!dm_list_empty(&seg->thin_messages)) { + dm_list_iterate_items(lmsg, &seg->thin_messages) { + dmsg.type = lmsg->type; + switch (lmsg->type) { + case DM_THIN_MESSAGE_CREATE_SNAP: + /* FIXME: to be implemented */ + log_debug("Thin pool create_snap %s.", lmsg->u.lv->name); + dmsg.u.m_create_snap.device_id = first_seg(lmsg->u.lv)->device_id; + dmsg.u.m_create_snap.origin_id = 0;//first_seg(first_seg(lmsg->u.lv)->origin)->device_id; + if (!dm_tree_node_add_thin_pool_message(node, &dmsg)) + return_0; + log_error(INTERNAL_ERROR "Sorry, not implemented yet."); + return 0; + case DM_THIN_MESSAGE_CREATE_THIN: + log_debug("Thin pool create_thin %s.", lmsg->u.lv->name); + dmsg.u.m_create_thin.device_id = first_seg(lmsg->u.lv)->device_id; + if (!dm_tree_node_add_thin_pool_message(node, &dmsg)) + return_0; + break; + case DM_THIN_MESSAGE_DELETE: + log_debug("Thin pool delete %u.", lmsg->u.delete_id); + dmsg.u.m_delete.device_id = lmsg->u.delete_id; + if (!dm_tree_node_add_thin_pool_message(node, &dmsg)) + return_0; + break; + case DM_THIN_MESSAGE_TRIM: + /* FIXME: to be implemented */ + log_error(INTERNAL_ERROR "Sorry, not implemented yet."); + return 0; + default: + log_error(INTERNAL_ERROR "Unsupported message."); + return 0; + } + } + + log_debug("Thin pool set_transaction_id %" PRIu64 ".", seg->transaction_id); + dmsg.type = DM_THIN_MESSAGE_SET_TRANSACTION_ID; + dmsg.u.m_set_transaction_id.current_id = seg->transaction_id - 1; + dmsg.u.m_set_transaction_id.new_id = seg->transaction_id; + if (!dm_tree_node_add_thin_pool_message(node, &dmsg)) + return_0; + } + return 1; } #endif