2 * Copyright (C) 2011-2012 Red Hat, Inc. All rights reserved.
4 * This file is part of LVM2.
6 * This copyrighted material is made available to anyone wishing to use,
7 * modify, copy, or redistribute it subject to the terms and conditions
8 * of the GNU Lesser General Public License v.2.1.
10 * You should have received a copy of the GNU Lesser General Public License
11 * along with this program; if not, write to the Free Software Foundation,
12 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 #include "toolcontext.h"
19 #include "text_export.h"
20 #include "text_import.h"
27 # include "sharedlib.h"
28 # include "libdevmapper-event.h"
31 /* Dm kernel module name for thin provisiong */
32 #define THIN_MODULE "thin-pool"
35 * Macro used as return argument - returns 0.
36 * return is left to be written in the function for better readability.
38 #define SEG_LOG_ERROR(t, p...) \
39 log_error(t " segment %s of logical volume %s.", ## p, \
40 dm_config_parent_name(sn), seg->lv->name), 0;
42 static int _thin_target_present(struct cmd_context
*cmd
,
43 const struct lv_segment
*seg
,
44 unsigned *attributes
);
46 static const char *_thin_pool_name(const struct lv_segment
*seg
)
48 return seg
->segtype
->name
;
51 static int _thin_pool_add_message(struct lv_segment
*seg
,
53 const struct dm_config_node
*sn
)
55 const char *lv_name
= NULL
;
56 struct logical_volume
*lv
= NULL
;
57 uint32_t delete_id
= 0;
58 dm_thin_message_t type
;
60 /* Message must have only one from: create, delete */
61 if (dm_config_get_str(sn
, "create", &lv_name
)) {
62 if (!(lv
= find_lv(seg
->lv
->vg
, lv_name
)))
63 return SEG_LOG_ERROR("Unknown LV %s for create message in",
65 /* FIXME: switch to _SNAP later, if the created LV has an origin */
66 type
= DM_THIN_MESSAGE_CREATE_THIN
;
69 if (!dm_config_get_uint32(sn
, "delete", &delete_id
)) {
71 return SEG_LOG_ERROR("Unknown message in");
74 return SEG_LOG_ERROR("Unsupported message format in");
75 type
= DM_THIN_MESSAGE_DELETE
;
78 if (!attach_pool_message(seg
, type
, lv
, delete_id
, 1))
84 static int _thin_pool_text_import(struct lv_segment
*seg
,
85 const struct dm_config_node
*sn
,
86 struct dm_hash_table
*pv_hash
__attribute__((unused
)))
89 struct logical_volume
*pool_data_lv
, *pool_metadata_lv
;
90 const char *discards_str
= NULL
;
92 if (!dm_config_get_str(sn
, "metadata", &lv_name
))
93 return SEG_LOG_ERROR("Metadata must be a string in");
95 if (!(pool_metadata_lv
= find_lv(seg
->lv
->vg
, lv_name
)))
96 return SEG_LOG_ERROR("Unknown metadata %s in", lv_name
);
98 if (!dm_config_get_str(sn
, "pool", &lv_name
))
99 return SEG_LOG_ERROR("Pool must be a string in");
101 if (!(pool_data_lv
= find_lv(seg
->lv
->vg
, lv_name
)))
102 return SEG_LOG_ERROR("Unknown pool %s in", lv_name
);
104 seg
->lv
->status
|= THIN_POOL
;
105 if (!attach_pool_metadata_lv(seg
, pool_metadata_lv
))
108 if (!attach_pool_data_lv(seg
, pool_data_lv
))
111 if (!dm_config_get_uint64(sn
, "transaction_id", &seg
->transaction_id
))
112 return SEG_LOG_ERROR("Could not read transaction_id for");
114 if (!dm_config_get_uint32(sn
, "chunk_size", &seg
->chunk_size
))
115 return SEG_LOG_ERROR("Could not read chunk_size");
117 if (dm_config_has_node(sn
, "discards") &&
118 !dm_config_get_str(sn
, "discards", &discards_str
))
119 return SEG_LOG_ERROR("Could not read discards for");
122 seg
->discards
= THIN_DISCARDS_PASSDOWN
;
123 else if (!get_pool_discards(discards_str
, &seg
->discards
))
124 return SEG_LOG_ERROR("Discards option unsupported for");
126 if (dm_config_has_node(sn
, "low_water_mark") &&
127 !dm_config_get_uint64(sn
, "low_water_mark", &seg
->low_water_mark
))
128 return SEG_LOG_ERROR("Could not read low_water_mark");
130 if ((seg
->chunk_size
< DM_THIN_MIN_DATA_BLOCK_SIZE
) ||
131 (seg
->chunk_size
> DM_THIN_MAX_DATA_BLOCK_SIZE
))
132 return SEG_LOG_ERROR("Unsupported value %u for chunk_size",
135 if (dm_config_has_node(sn
, "zero_new_blocks") &&
136 !dm_config_get_uint32(sn
, "zero_new_blocks", &seg
->zero_new_blocks
))
137 return SEG_LOG_ERROR("Could not read zero_new_blocks for");
140 for (; sn
; sn
= sn
->sib
)
141 if (!(sn
->v
) && !_thin_pool_add_message(seg
, sn
->key
, sn
->child
))
147 static int _thin_pool_text_import_area_count(const struct dm_config_node
*sn
,
148 uint32_t *area_count
)
155 static int _thin_pool_text_export(const struct lv_segment
*seg
, struct formatter
*f
)
158 const struct lv_thin_message
*tmsg
;
160 outf(f
, "metadata = \"%s\"", seg
->metadata_lv
->name
);
161 outf(f
, "pool = \"%s\"", seg_lv(seg
, 0)->name
);
162 outf(f
, "transaction_id = %" PRIu64
, seg
->transaction_id
);
163 outsize(f
, (uint64_t) seg
->chunk_size
,
164 "chunk_size = %u", seg
->chunk_size
);
166 switch (seg
->discards
) {
167 case THIN_DISCARDS_PASSDOWN
:
168 case THIN_DISCARDS_NO_PASSDOWN
:
169 case THIN_DISCARDS_IGNORE
:
170 outf(f
, "discards = \"%s\"", get_pool_discards_name(seg
->discards
));
173 log_error(INTERNAL_ERROR
"Invalid discards value %d.", seg
->discards
);
177 if (seg
->low_water_mark
)
178 outf(f
, "low_water_mark = %" PRIu64
, seg
->low_water_mark
);
180 if (seg
->zero_new_blocks
)
181 outf(f
, "zero_new_blocks = 1");
183 dm_list_iterate_items(tmsg
, &seg
->thin_messages
) {
184 /* Extra validation */
185 switch (tmsg
->type
) {
186 case DM_THIN_MESSAGE_CREATE_SNAP
:
187 case DM_THIN_MESSAGE_CREATE_THIN
:
188 if (!lv_is_thin_volume(tmsg
->u
.lv
)) {
189 log_error(INTERNAL_ERROR
190 "LV %s is not a thin volume.",
202 outf(f
, "message%d {", ++cnt
);
205 switch (tmsg
->type
) {
206 case DM_THIN_MESSAGE_CREATE_SNAP
:
207 case DM_THIN_MESSAGE_CREATE_THIN
:
208 outf(f
, "create = \"%s\"", tmsg
->u
.lv
->name
);
210 case DM_THIN_MESSAGE_DELETE
:
211 outf(f
, "delete = %d", tmsg
->u
.delete_id
);
214 log_error(INTERNAL_ERROR
"Passed unsupported message.");
225 #ifdef DEVMAPPER_SUPPORT
226 static int _thin_pool_add_target_line(struct dev_manager
*dm
,
228 struct cmd_context
*cmd
,
229 void **target_state
__attribute__((unused
)),
230 struct lv_segment
*seg
,
231 const struct lv_activate_opts
*laopts
,
232 struct dm_tree_node
*node
, uint64_t len
,
233 uint32_t *pvmove_mirror_count
__attribute__((unused
)))
235 static int _no_discards
= 0;
236 char *metadata_dlid
, *pool_dlid
;
237 const struct lv_thin_message
*lmsg
;
238 const struct logical_volume
*origin
;
240 uint64_t transaction_id
= 0;
243 if (!_thin_target_present(cmd
, seg
, &attr
))
246 if (!laopts
->real_pool
) {
247 if (!(pool_dlid
= build_dm_uuid(mem
, seg
->lv
->lvid
.s
, "tpool"))) {
248 log_error("Failed to build uuid for thin pool LV %s.", seg
->pool_lv
->name
);
252 if (!add_linear_area_to_dtree(node
, len
, seg
->lv
->vg
->extent_size
,
253 cmd
->use_linear_target
,
254 seg
->lv
->vg
->name
, seg
->lv
->name
) ||
255 !dm_tree_node_add_target_area(node
, NULL
, pool_dlid
, 0))
261 if (!(metadata_dlid
= build_dm_uuid(mem
, seg
->metadata_lv
->lvid
.s
, NULL
))) {
262 log_error("Failed to build uuid for metadata LV %s.",
263 seg
->metadata_lv
->name
);
267 if (!(pool_dlid
= build_dm_uuid(mem
, seg_lv(seg
, 0)->lvid
.s
, NULL
))) {
268 log_error("Failed to build uuid for pool LV %s.",
269 seg_lv(seg
, 0)->name
);
273 if (!dm_tree_node_add_thin_pool_target(node
, len
, seg
->transaction_id
,
274 metadata_dlid
, pool_dlid
,
275 seg
->chunk_size
, seg
->low_water_mark
,
276 seg
->zero_new_blocks
? 0 : 1))
279 if ((seg
->discards
!= THIN_DISCARDS_PASSDOWN
) && (attr
& THIN_FEATURE_DISCARDS
)) {
280 /* FIXME: Check whether underlying dev supports discards */
281 if (!dm_tree_node_set_thin_pool_discard(node
,
282 seg
->discards
== THIN_DISCARDS_IGNORE
,
283 seg
->discards
== THIN_DISCARDS_NO_PASSDOWN
))
286 log_warn_suppress(_no_discards
++, "WARNING: Thin pool target does "
287 "not support discards (needs kernel >= 3.4).");
290 * Add messages only for activation tree.
291 * Otherwise avoid checking for existence of suspended origin.
292 * Also transation_id is checked only when snapshot origin is active.
293 * (This might change later)
295 if (!laopts
->is_activate
)
298 dm_list_iterate_items(lmsg
, &seg
->thin_messages
) {
299 switch (lmsg
->type
) {
300 case DM_THIN_MESSAGE_CREATE_THIN
:
301 origin
= first_seg(lmsg
->u
.lv
)->origin
;
302 /* Check if the origin is suspended */
303 if (origin
&& lv_info(cmd
, origin
, 0, &info
, 0, 0) &&
304 info
.exists
&& !info
.suspended
) {
305 /* Origin is not suspended, but the transaction may have been
306 * already transfered, so test for transaction_id and
307 * allow to pass in the message for dmtree processing
308 * so it will skip all messages later.
310 if (!lv_thin_pool_transaction_id(seg
->lv
, &transaction_id
))
311 return_0
; /* Thin pool should exist and work */
312 if (transaction_id
!= seg
->transaction_id
) {
313 log_error("Can't create snapshot %s as origin %s is not suspended.",
314 lmsg
->u
.lv
->name
, origin
->name
);
318 log_debug("Thin pool create_%s %s.", (!origin
) ? "thin" : "snap", lmsg
->u
.lv
->name
);
319 if (!dm_tree_node_add_thin_pool_message(node
,
320 (!origin
) ? lmsg
->type
: DM_THIN_MESSAGE_CREATE_SNAP
,
321 first_seg(lmsg
->u
.lv
)->device_id
,
322 (!origin
) ? 0 : first_seg(origin
)->device_id
))
325 case DM_THIN_MESSAGE_DELETE
:
326 log_debug("Thin pool delete %u.", lmsg
->u
.delete_id
);
327 if (!dm_tree_node_add_thin_pool_message(node
,
329 lmsg
->u
.delete_id
, 0))
333 log_error(INTERNAL_ERROR
"Unsupported message.");
338 if (!dm_list_empty(&seg
->thin_messages
)) {
339 /* Messages were passed, modify transaction_id as the last one */
340 log_debug("Thin pool set transaction id %" PRIu64
".", seg
->transaction_id
);
341 if (!dm_tree_node_add_thin_pool_message(node
,
342 DM_THIN_MESSAGE_SET_TRANSACTION_ID
,
343 seg
->transaction_id
- 1,
344 seg
->transaction_id
))
351 static int _thin_pool_target_percent(void **target_state
__attribute__((unused
)),
354 struct cmd_context
*cmd
__attribute__((unused
)),
355 struct lv_segment
*seg
,
357 uint64_t *total_numerator
,
358 uint64_t *total_denominator
)
360 struct dm_status_thin_pool
*s
;
362 if (!dm_get_status_thin_pool(mem
, params
, &s
))
365 /* With 'seg' report metadata percent, otherwice data percent */
367 *percent
= make_percent(s
->used_metadata_blocks
,
368 s
->total_metadata_blocks
);
369 *total_numerator
+= s
->used_metadata_blocks
;
370 *total_denominator
+= s
->total_metadata_blocks
;
372 *percent
= make_percent(s
->used_data_blocks
,
373 s
->total_data_blocks
);
374 *total_numerator
+= s
->used_data_blocks
;
375 *total_denominator
+= s
->total_data_blocks
;
382 static const char *_get_thin_dso_path(struct cmd_context
*cmd
)
384 return get_monitor_dso_path(cmd
, find_config_tree_str(cmd
, "dmeventd/thin_library",
385 DEFAULT_DMEVENTD_THIN_LIB
));
388 /* FIXME Cache this */
389 static int _target_registered(struct lv_segment
*seg
, int *pending
)
391 return target_registered_with_dmeventd(seg
->lv
->vg
->cmd
,
392 _get_thin_dso_path(seg
->lv
->vg
->cmd
),
396 /* FIXME This gets run while suspended and performs banned operations. */
397 static int _target_set_events(struct lv_segment
*seg
, int evmask
, int set
)
399 /* FIXME Make timeout (10) configurable */
400 return target_register_events(seg
->lv
->vg
->cmd
,
401 _get_thin_dso_path(seg
->lv
->vg
->cmd
),
402 seg
->lv
, evmask
, set
, 10);
405 static int _target_register_events(struct lv_segment
*seg
,
408 return _target_set_events(seg
, events
, 1);
411 static int _target_unregister_events(struct lv_segment
*seg
,
414 return _target_set_events(seg
, events
, 0);
416 # endif /* DMEVENTD */
417 #endif /* DEVMAPPER_SUPPORT */
419 static const char *_thin_name(const struct lv_segment
*seg
)
421 return seg
->segtype
->name
;
424 static int _thin_text_import(struct lv_segment
*seg
,
425 const struct dm_config_node
*sn
,
426 struct dm_hash_table
*pv_hash
__attribute__((unused
)))
429 struct logical_volume
*pool_lv
, *origin
= NULL
;
431 if (!dm_config_get_str(sn
, "thin_pool", &lv_name
))
432 return SEG_LOG_ERROR("Thin pool must be a string in");
434 if (!(pool_lv
= find_lv(seg
->lv
->vg
, lv_name
)))
435 return SEG_LOG_ERROR("Unknown thin pool %s in", lv_name
);
437 if (!dm_config_get_uint64(sn
, "transaction_id", &seg
->transaction_id
))
438 return SEG_LOG_ERROR("Could not read transaction_id for");
440 if (dm_config_has_node(sn
, "origin")) {
441 if (!dm_config_get_str(sn
, "origin", &lv_name
))
442 return SEG_LOG_ERROR("Origin must be a string in");
444 if (!(origin
= find_lv(seg
->lv
->vg
, lv_name
)))
445 return SEG_LOG_ERROR("Unknown origin %s in", lv_name
);
448 if (!dm_config_get_uint32(sn
, "device_id", &seg
->device_id
))
449 return SEG_LOG_ERROR("Could not read device_id for");
451 if (seg
->device_id
> DM_THIN_MAX_DEVICE_ID
)
452 return SEG_LOG_ERROR("Unsupported value %u for device_id",
455 if (!attach_pool_lv(seg
, pool_lv
, origin
))
461 static int _thin_text_export(const struct lv_segment
*seg
, struct formatter
*f
)
463 outf(f
, "thin_pool = \"%s\"", seg
->pool_lv
->name
);
464 outf(f
, "transaction_id = %" PRIu64
, seg
->transaction_id
);
465 outf(f
, "device_id = %d", seg
->device_id
);
468 outf(f
, "origin = \"%s\"", seg
->origin
->name
);
473 #ifdef DEVMAPPER_SUPPORT
474 static int _thin_add_target_line(struct dev_manager
*dm
,
476 struct cmd_context
*cmd
__attribute__((unused
)),
477 void **target_state
__attribute__((unused
)),
478 struct lv_segment
*seg
,
479 const struct lv_activate_opts
*laopts
__attribute__((unused
)),
480 struct dm_tree_node
*node
, uint64_t len
,
481 uint32_t *pvmove_mirror_count
__attribute__((unused
)))
484 uint32_t device_id
= seg
->device_id
;
486 if (!(pool_dlid
= build_dm_uuid(mem
, seg
->pool_lv
->lvid
.s
, "tpool"))) {
487 log_error("Failed to build uuid for pool LV %s.",
492 if (!dm_tree_node_add_thin_target(node
, len
, pool_dlid
, device_id
))
498 static int _thin_target_percent(void **target_state
__attribute__((unused
)),
501 struct cmd_context
*cmd
__attribute__((unused
)),
502 struct lv_segment
*seg
,
504 uint64_t *total_numerator
,
505 uint64_t *total_denominator
)
507 struct dm_status_thin
*s
;
509 /* Status for thin device is in sectors */
510 if (!dm_get_status_thin(mem
, params
, &s
))
514 *percent
= make_percent(s
->mapped_sectors
, seg
->lv
->size
);
515 *total_denominator
+= seg
->lv
->size
;
517 /* No lv_segment info here */
518 *percent
= PERCENT_INVALID
;
519 /* FIXME: Using denominator to pass the mapped info upward? */
520 *total_denominator
+= s
->highest_mapped_sector
;
523 *total_numerator
+= s
->mapped_sectors
;
528 static int _thin_target_present(struct cmd_context
*cmd
,
529 const struct lv_segment
*seg
,
530 unsigned *attributes
)
532 static int _checked
= 0;
533 static int _present
= 0;
534 static int _attrs
= 0;
535 uint32_t maj
, min
, patchlevel
;
538 _present
= target_present(cmd
, THIN_MODULE
, 1);
540 if (!target_version(THIN_MODULE
, &maj
, &min
, &patchlevel
)) {
541 log_error("Cannot read " THIN_MODULE
" target version.");
545 if (maj
>=1 && min
>= 1)
546 _attrs
|= THIN_FEATURE_DISCARDS
;
548 /* FIXME Log this as WARNING later only if the user asked for the feature to be used but it's not present */
549 log_debug("Target " THIN_MODULE
" does not support discards.");
551 if (maj
>=1 && min
>= 1)
552 _attrs
|= THIN_FEATURE_EXTERNAL_ORIGIN
;
554 /* FIXME Log this as WARNING later only if the user asked for the feature to be used but it's not present */
555 log_debug("Target " THIN_MODULE
" does not support external origins.");
557 if (maj
>=1 && min
>= 1)
558 _attrs
|= THIN_FEATURE_BLOCK_SIZE
;
560 /* FIXME Log this as WARNING later only if the user asked for the feature to be used but it's not present */
561 log_debug("Target " THIN_MODULE
" does not support non power of 2 block sizes.");
567 *attributes
= _attrs
;
573 static int _thin_modules_needed(struct dm_pool
*mem
,
574 const struct lv_segment
*seg
__attribute__((unused
)),
575 struct dm_list
*modules
)
577 if (!str_list_add(mem
, modules
, THIN_MODULE
)) {
578 log_error("thin string list allocation failed");
585 static void _thin_destroy(struct segment_type
*segtype
)
590 static struct segtype_handler _thin_pool_ops
= {
591 .name
= _thin_pool_name
,
592 .text_import
= _thin_pool_text_import
,
593 .text_import_area_count
= _thin_pool_text_import_area_count
,
594 .text_export
= _thin_pool_text_export
,
595 #ifdef DEVMAPPER_SUPPORT
596 .add_target_line
= _thin_pool_add_target_line
,
597 .target_percent
= _thin_pool_target_percent
,
598 .target_present
= _thin_target_present
,
600 .target_monitored
= _target_registered
,
601 .target_monitor_events
= _target_register_events
,
602 .target_unmonitor_events
= _target_unregister_events
,
603 # endif /* DMEVENTD */
605 .modules_needed
= _thin_modules_needed
,
606 .destroy
= _thin_destroy
,
609 static struct segtype_handler _thin_ops
= {
611 .text_import
= _thin_text_import
,
612 .text_export
= _thin_text_export
,
613 #ifdef DEVMAPPER_SUPPORT
614 .add_target_line
= _thin_add_target_line
,
615 .target_percent
= _thin_target_percent
,
616 .target_present
= _thin_target_present
,
618 .modules_needed
= _thin_modules_needed
,
619 .destroy
= _thin_destroy
,
623 int init_thin_segtypes(struct cmd_context
*cmd
, struct segtype_library
*seglib
)
625 int init_multiple_segtypes(struct cmd_context
*cmd
, struct segtype_library
*seglib
);
626 int init_multiple_segtypes(struct cmd_context
*cmd
, struct segtype_library
*seglib
)
629 static const struct {
630 struct segtype_handler
*ops
;
634 { &_thin_pool_ops
, "thin-pool", SEG_THIN_POOL
},
635 /* FIXME Maybe use SEG_THIN_VOLUME instead of SEG_VIRTUAL */
636 { &_thin_ops
, "thin", SEG_THIN_VOLUME
| SEG_VIRTUAL
}
639 struct segment_type
*segtype
;
642 for (i
= 0; i
< sizeof(reg_segtypes
)/sizeof(reg_segtypes
[0]); ++i
) {
643 segtype
= dm_zalloc(sizeof(*segtype
));
646 log_error("Failed to allocate memory for %s segtype",
647 reg_segtypes
[i
].name
);
651 segtype
->ops
= reg_segtypes
[i
].ops
;
652 segtype
->name
= reg_segtypes
[i
].name
;
653 segtype
->flags
= reg_segtypes
[i
].flags
;
655 #ifdef DEVMAPPER_SUPPORT
657 if ((reg_segtypes
[i
].flags
& SEG_THIN_POOL
) &&
658 _get_thin_dso_path(cmd
))
659 segtype
->flags
|= SEG_MONITORED
;
660 # endif /* DMEVENTD */
662 if (!lvm_register_segtype(seglib
, segtype
))
663 /* segtype is already destroyed */
666 log_very_verbose("Initialised segtype: %s", segtype
->name
);