Line data Source code
1 : /*
2 : * Copyright (C) 2014-2025 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : */
18 :
19 : #include <glib.h>
20 : #include <math.h>
21 : #include <stdio.h>
22 : #include <libdevmapper.h>
23 :
24 : #include "lvm.h"
25 : #include "lvm-private.h"
26 : #include "check_deps.h"
27 : #include "dm_logging.h"
28 : #include "vdo_stats.h"
29 :
30 :
31 : #define INT_FLOAT_EPS 1e-5
32 :
33 : #define MIN_PE_SIZE (1 KiB)
34 : #define MAX_PE_SIZE (16 GiB)
35 :
36 : #define VDO_POOL_SUFFIX "vpool"
37 :
38 : #define THPOOL_MD_FACTOR_NEW (0.2)
39 : #define THPOOL_MD_FACTOR_EXISTS (1 / 6.0)
40 :
41 : #define MIN_THPOOL_MD_SIZE (4 MiB)
42 : /* DM_THIN_MAX_METADATA_SIZE is in 512 sectors */
43 : #define MAX_THPOOL_MD_SIZE (DM_THIN_MAX_METADATA_SIZE * 512)
44 :
45 : #define MIN_THPOOL_CHUNK_SIZE (64 KiB)
46 : #define MAX_THPOOL_CHUNK_SIZE (1 GiB)
47 : #define DEFAULT_CHUNK_SIZE (64 KiB)
48 :
49 : /* according to lvmcache (7) */
50 : #define MIN_CACHE_MD_SIZE (8 MiB)
51 :
52 : #ifdef __LP64__
53 : /* 64bit system */
54 : #define MAX_LV_SIZE (8 EiB)
55 : #else
56 : /* 32bit system */
57 : #define MAX_LV_SIZE (16 TiB)
58 : #endif
59 :
60 : GMutex global_config_lock;
61 : gchar *global_config_str = NULL;
62 : gchar *global_devices_str = NULL;
63 :
64 : /**
65 : * bd_lvm_is_supported_pe_size:
66 : * @size: size (in bytes) to test
67 : * @error: (out) (optional): place to store error (if any)
68 : *
69 : * Returns: whether the given size is supported physical extent size or not
70 : *
71 : * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
72 : */
73 71 : gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error G_GNUC_UNUSED) {
74 71 : return (((size % 2) == 0) && (size >= (MIN_PE_SIZE)) && (size <= (MAX_PE_SIZE)));
75 : }
76 :
77 : /**
78 : * bd_lvm_get_supported_pe_sizes:
79 : * @error: (out) (optional): place to store error (if any)
80 : *
81 : * Returns: (transfer full) (array fixed-size=25): list of supported PE sizes
82 : *
83 : * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
84 : */
85 2 : guint64 *bd_lvm_get_supported_pe_sizes (GError **error G_GNUC_UNUSED) {
86 : guint8 i;
87 2 : guint64 val = MIN_PE_SIZE;
88 2 : guint8 num_items = ((guint8) round (log2 ((double) MAX_PE_SIZE))) - ((guint8) round (log2 ((double) MIN_PE_SIZE))) + 2;
89 2 : guint64 *ret = g_new0 (guint64, num_items);
90 :
91 52 : for (i=0; (val <= MAX_PE_SIZE); i++, val = val * 2)
92 50 : ret[i] = val;
93 :
94 2 : ret[num_items-1] = 0;
95 :
96 2 : return ret;
97 : }
98 :
99 : /**
100 : * bd_lvm_get_max_lv_size:
101 : * @error: (out) (optional): place to store error (if any)
102 : *
103 : * Returns: maximum LV size in bytes
104 : *
105 : * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
106 : */
107 13 : guint64 bd_lvm_get_max_lv_size (GError **error G_GNUC_UNUSED) {
108 13 : return MAX_LV_SIZE;
109 : }
110 :
111 : /**
112 : * bd_lvm_round_size_to_pe:
113 : * @size: size to be rounded
114 : * @pe_size: physical extent (PE) size or 0 to use the default
115 : * @roundup: whether to round up or down (ceil or floor)
116 : * @error: (out) (optional): place to store error (if any)
117 : *
118 : * Returns: @size rounded to @pe_size according to the @roundup
119 : *
120 : * Rounds given @size up/down to a multiple of @pe_size according to the value
121 : * of the @roundup parameter. If the rounded value is too big to fit in the
122 : * return type, the result is rounded down (floored) regardless of the @roundup
123 : * parameter.
124 : *
125 : * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
126 : */
127 47 : guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error G_GNUC_UNUSED) {
128 47 : pe_size = RESOLVE_PE_SIZE (pe_size);
129 47 : guint64 delta = size % pe_size;
130 47 : if (delta == 0)
131 9 : return size;
132 :
133 38 : if (roundup && (((G_MAXUINT64 - (pe_size - delta)) >= size)))
134 26 : return size + (pe_size - delta);
135 : else
136 12 : return size - delta;
137 : }
138 :
139 : /**
140 : * bd_lvm_get_lv_physical_size:
141 : * @lv_size: LV size
142 : * @pe_size: PE size
143 : * @error: (out) (optional): place to store error (if any)
144 : *
145 : * Returns: space taken on disk(s) by the LV with given @size
146 : *
147 : * Gives number of bytes needed for an LV with the size @lv_size on an LVM stack
148 : * using given @pe_size.
149 : *
150 : * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
151 : */
152 6 : guint64 bd_lvm_get_lv_physical_size (guint64 lv_size, guint64 pe_size, GError **error) {
153 6 : pe_size = RESOLVE_PE_SIZE (pe_size);
154 :
155 : /* the LV just takes space rounded up to the multiple of extent size */
156 6 : return bd_lvm_round_size_to_pe (lv_size, pe_size, TRUE, error);
157 : }
158 :
159 : /**
160 : * bd_lvm_get_thpool_padding:
161 : * @size: size of the thin pool
162 : * @pe_size: PE size or 0 if the default value should be used
163 : * @included: if padding is already included in the size
164 : * @error: (out) (optional): place to store error (if any)
165 : *
166 : * Returns: size of the padding needed for a thin pool with the given @size
167 : * according to the @pe_size and @included
168 : *
169 : * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
170 : */
171 5 : guint64 bd_lvm_get_thpool_padding (guint64 size, guint64 pe_size, gboolean included, GError **error) {
172 : guint64 raw_md_size;
173 5 : pe_size = RESOLVE_PE_SIZE (pe_size);
174 :
175 5 : if (included)
176 2 : raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_EXISTS);
177 : else
178 3 : raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_NEW);
179 :
180 5 : return MIN (bd_lvm_round_size_to_pe (raw_md_size, pe_size, TRUE, error),
181 : bd_lvm_round_size_to_pe (MAX_THPOOL_MD_SIZE, pe_size, TRUE, error));
182 : }
183 :
184 : /**
185 : * bd_lvm_get_thpool_meta_size:
186 : * @size: size of the thin pool
187 : * @chunk_size: chunk size of the thin pool or 0 to use the default
188 : * @n_snapshots: ignored
189 : * @error: (out) (optional): place to store error (if any)
190 : *
191 : * Note: This function will be changed in 3.0: the @n_snapshots parameter
192 : * is currently not used and will be removed.
193 : *
194 : * Returns: recommended size of the metadata space for the specified pool
195 : *
196 : * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
197 : */
198 6 : guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) {
199 6 : guint64 md_size = 0;
200 :
201 : /* based on lvcreate metadata size calculation */
202 6 : md_size = UINT64_C (64) * size / (chunk_size ? chunk_size : DEFAULT_CHUNK_SIZE);
203 :
204 6 : if (md_size > MAX_THPOOL_MD_SIZE)
205 0 : md_size = MAX_THPOOL_MD_SIZE;
206 6 : else if (md_size < MIN_THPOOL_MD_SIZE)
207 2 : md_size = MIN_THPOOL_MD_SIZE;
208 :
209 6 : return md_size;
210 : }
211 :
212 : /**
213 : * bd_lvm_is_valid_thpool_md_size:
214 : * @size: the size to be tested
215 : * @error: (out) (optional): place to store error (if any)
216 : *
217 : * Returns: whether the given size is a valid thin pool metadata size or not
218 : *
219 : * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
220 : */
221 14 : gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error G_GNUC_UNUSED) {
222 14 : return ((MIN_THPOOL_MD_SIZE <= size) && (size <= MAX_THPOOL_MD_SIZE));
223 : }
224 :
225 : /**
226 : * bd_lvm_is_valid_thpool_chunk_size:
227 : * @size: the size to be tested
228 : * @discard: whether discard/TRIM is required to be supported or not
229 : * @error: (out) (optional): place to store error (if any)
230 : *
231 : * Returns: whether the given size is a valid thin pool chunk size or not
232 : *
233 : * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
234 : */
235 12 : gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error G_GNUC_UNUSED) {
236 12 : gdouble size_log2 = 0.0;
237 :
238 12 : if ((size < MIN_THPOOL_CHUNK_SIZE) || (size > MAX_THPOOL_CHUNK_SIZE))
239 0 : return FALSE;
240 :
241 : /* To support discard, chunk size must be a power of two. Otherwise it must be a
242 : multiple of 64 KiB. */
243 12 : if (discard) {
244 6 : size_log2 = log2 ((double) size);
245 6 : return ABS (((int) round (size_log2)) - size_log2) <= INT_FLOAT_EPS;
246 : } else
247 6 : return (size % (64 KiB)) == 0;
248 : }
249 :
250 : /**
251 : * bd_lvm_cache_get_default_md_size:
252 : * @cache_size: size of the cache to determine MD size for
253 : * @error: (out) (optional): place to store error (if any)
254 : *
255 : * Returns: recommended default size of the cache metadata LV or 0 in case of error
256 : *
257 : * Tech category: %BD_LVM_TECH_CACHE_CALCS no mode (it is ignored)
258 : */
259 20 : guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error G_GNUC_UNUSED) {
260 20 : return MAX ((guint64) cache_size / 1000, MIN_CACHE_MD_SIZE);
261 : }
262 :
263 : /**
264 : * bd_lvm_set_global_config:
265 : * @new_config: (nullable): string representation of the new global libblockdev LVM
266 : * configuration to set or %NULL to reset to default
267 : * @error: (out) (optional): place to store error (if any)
268 : *
269 : *
270 : * Note: This function sets configuration options for LVM calls internally
271 : * in libblockdev, it doesn't change the global lvm.conf config file.
272 : * Calling this function with `backup {backup=0 archive=0}` for example
273 : * means `--config=backup {backup=0 archive=0}"` will be added to all
274 : * calls libblockdev makes.
275 : *
276 : * Returns: whether the new requested global config @new_config was successfully
277 : * set or not
278 : *
279 : * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
280 : */
281 34 : gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error G_GNUC_UNUSED) {
282 : /* XXX: the error attribute will likely be used in the future when
283 : some validation comes into the game */
284 :
285 34 : g_mutex_lock (&global_config_lock);
286 :
287 : /* first free the old value */
288 34 : g_free (global_config_str);
289 :
290 : /* now store the new one */
291 34 : if (!new_config || g_strcmp0 (new_config, "") == 0)
292 18 : global_config_str = NULL;
293 : else
294 16 : global_config_str = g_strdup (new_config);
295 :
296 34 : g_mutex_unlock (&global_config_lock);
297 34 : return TRUE;
298 : }
299 :
300 : /**
301 : * bd_lvm_get_global_config:
302 : * @error: (out) (optional): place to store error (if any)
303 : *
304 : * Returns: (transfer full): a copy of a string representation of the currently
305 : * set libblockdev LVM global configuration
306 : *
307 : * Note: This function does not change the global `lvm.conf` config
308 : * file, see %bd_lvm_set_global_config for details.
309 : *
310 : * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
311 : */
312 10 : gchar* bd_lvm_get_global_config (GError **error G_GNUC_UNUSED) {
313 10 : gchar *ret = NULL;
314 :
315 10 : g_mutex_lock (&global_config_lock);
316 10 : ret = g_strdup (global_config_str ? global_config_str : "");
317 10 : g_mutex_unlock (&global_config_lock);
318 :
319 10 : return ret;
320 : }
321 :
322 : /**
323 : * bd_lvm_set_devices_filter:
324 : * @devices: (nullable) (array zero-terminated=1): list of devices for lvm commands to work on
325 : * @error: (out) (optional): place to store error (if any)
326 : *
327 : * Returns: whether the devices filter was successfully set or not
328 : *
329 : * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
330 : */
331 12 : gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error) {
332 12 : if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
333 0 : return FALSE;
334 :
335 12 : g_mutex_lock (&global_config_lock);
336 :
337 : /* first free the old value */
338 12 : g_free (global_devices_str);
339 :
340 : /* now store the new one */
341 12 : if (!devices || !(*devices))
342 4 : global_devices_str = NULL;
343 : else
344 8 : global_devices_str = g_strjoinv (",", (gchar **) devices);
345 :
346 12 : g_mutex_unlock (&global_config_lock);
347 12 : return TRUE;
348 : }
349 :
350 : /**
351 : * bd_lvm_get_devices_filter:
352 : * @error: (out) (optional): place to store error (if any)
353 : *
354 : * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
355 : * the currently set LVM devices filter
356 : *
357 : * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
358 : */
359 8 : gchar** bd_lvm_get_devices_filter (GError **error G_GNUC_UNUSED) {
360 8 : gchar **ret = NULL;
361 :
362 8 : g_mutex_lock (&global_config_lock);
363 :
364 8 : if (global_devices_str)
365 4 : ret = g_strsplit (global_devices_str, ",", -1);
366 : else
367 4 : ret = NULL;
368 :
369 8 : g_mutex_unlock (&global_config_lock);
370 :
371 8 : return ret;
372 : }
373 :
374 : /**
375 : * bd_lvm_cache_get_mode_str:
376 : * @mode: mode to get the string representation for
377 : * @error: (out) (optional): place to store error (if any)
378 : *
379 : * Returns: string representation of @mode or %NULL in case of error
380 : *
381 : * Tech category: always provided/supported
382 : */
383 22 : const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error) {
384 22 : if (mode == BD_LVM_CACHE_MODE_WRITETHROUGH)
385 14 : return "writethrough";
386 8 : else if (mode == BD_LVM_CACHE_MODE_WRITEBACK)
387 6 : return "writeback";
388 2 : else if (mode == BD_LVM_CACHE_MODE_UNKNOWN)
389 2 : return "unknown";
390 : else {
391 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
392 : "Invalid mode given: %d", mode);
393 0 : return NULL;
394 : }
395 : }
396 :
397 : /**
398 : * bd_lvm_cache_get_mode_from_str:
399 : * @mode_str: string representation of a cache mode
400 : * @error: (out) (optional): place to store error (if any)
401 : *
402 : * Returns: cache mode for the @mode_str or %BD_LVM_CACHE_MODE_UNKNOWN if
403 : * failed to determine
404 : *
405 : * Tech category: always provided/supported
406 : */
407 8 : BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error) {
408 8 : if (g_strcmp0 (mode_str, "writethrough") == 0)
409 2 : return BD_LVM_CACHE_MODE_WRITETHROUGH;
410 6 : else if (g_strcmp0 (mode_str, "writeback") == 0)
411 2 : return BD_LVM_CACHE_MODE_WRITEBACK;
412 4 : else if (g_strcmp0 (mode_str, "unknown") == 0)
413 2 : return BD_LVM_CACHE_MODE_UNKNOWN;
414 : else {
415 2 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
416 : "Invalid mode given: %s", mode_str);
417 2 : return BD_LVM_CACHE_MODE_UNKNOWN;
418 : }
419 : }
420 :
421 : /**
422 : * bd_lvm_get_vdo_operating_mode_str:
423 : * @mode: mode to get the string representation for
424 : * @error: (out) (optional): place to store error (if any)
425 : *
426 : * Returns: string representation of @mode or %NULL in case of error
427 : *
428 : * Tech category: always provided/supported
429 : */
430 2 : const gchar* bd_lvm_get_vdo_operating_mode_str (BDLVMVDOOperatingMode mode, GError **error) {
431 2 : switch (mode) {
432 0 : case BD_LVM_VDO_MODE_RECOVERING:
433 0 : return "recovering";
434 0 : case BD_LVM_VDO_MODE_READ_ONLY:
435 0 : return "read-only";
436 2 : case BD_LVM_VDO_MODE_NORMAL:
437 2 : return "normal";
438 0 : case BD_LVM_VDO_MODE_UNKNOWN:
439 0 : return "unknown";
440 0 : default:
441 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
442 : "Invalid LVM VDO operating mode.");
443 0 : return NULL;
444 : }
445 : }
446 :
447 : /**
448 : * bd_lvm_get_vdo_compression_state_str:
449 : * @state: state to get the string representation for
450 : * @error: (out) (optional): place to store error (if any)
451 : *
452 : * Returns: string representation of @state or %NULL in case of error
453 : *
454 : * Tech category: always provided/supported
455 : */
456 4 : const gchar* bd_lvm_get_vdo_compression_state_str (BDLVMVDOCompressionState state, GError **error) {
457 4 : switch (state) {
458 4 : case BD_LVM_VDO_COMPRESSION_ONLINE:
459 4 : return "online";
460 0 : case BD_LVM_VDO_COMPRESSION_OFFLINE:
461 0 : return "offline";
462 0 : case BD_LVM_VDO_COMPRESSION_UNKNOWN:
463 0 : return "unknown";
464 0 : default:
465 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
466 : "Invalid LVM VDO compression state.");
467 0 : return NULL;
468 : }
469 : }
470 :
471 : /**
472 : * bd_lvm_get_vdo_index_state_str:
473 : * @state: state to get the string representation for
474 : * @error: (out) (optional): place to store error (if any)
475 : *
476 : * Returns: string representation of @state or %NULL in case of error
477 : *
478 : * Tech category: always provided/supported
479 : */
480 2 : const gchar* bd_lvm_get_vdo_index_state_str (BDLVMVDOIndexState state, GError **error) {
481 2 : switch (state) {
482 0 : case BD_LVM_VDO_INDEX_ERROR:
483 0 : return "error";
484 0 : case BD_LVM_VDO_INDEX_CLOSED:
485 0 : return "closed";
486 1 : case BD_LVM_VDO_INDEX_OPENING:
487 1 : return "opening";
488 0 : case BD_LVM_VDO_INDEX_CLOSING:
489 0 : return "closing";
490 0 : case BD_LVM_VDO_INDEX_OFFLINE:
491 0 : return "offline";
492 1 : case BD_LVM_VDO_INDEX_ONLINE:
493 1 : return "online";
494 0 : case BD_LVM_VDO_INDEX_UNKNOWN:
495 0 : return "unknown";
496 0 : default:
497 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
498 : "Invalid LVM VDO index state.");
499 0 : return NULL;
500 : }
501 : }
502 :
503 : /**
504 : * bd_lvm_get_vdo_write_policy_str:
505 : * @policy: policy to get the string representation for
506 : * @error: (out) (optional): place to store error (if any)
507 : *
508 : * Returns: string representation of @policy or %NULL in case of error
509 : *
510 : * Tech category: always provided/supported
511 : */
512 20 : const gchar* bd_lvm_get_vdo_write_policy_str (BDLVMVDOWritePolicy policy, GError **error) {
513 20 : switch (policy) {
514 16 : case BD_LVM_VDO_WRITE_POLICY_AUTO:
515 16 : return "auto";
516 4 : case BD_LVM_VDO_WRITE_POLICY_SYNC:
517 4 : return "sync";
518 0 : case BD_LVM_VDO_WRITE_POLICY_ASYNC:
519 0 : return "async";
520 0 : case BD_LVM_VDO_WRITE_POLICY_UNKNOWN:
521 0 : return "unknown";
522 0 : default:
523 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
524 : "Invalid LVM VDO write policy.");
525 0 : return NULL;
526 : }
527 : }
528 :
529 : /**
530 : * bd_lvm_get_vdo_write_policy_from_str:
531 : * @policy_str: string representation of a policy
532 : * @error: (out) (optional): place to store error (if any)
533 : *
534 : * Returns: write policy for the @policy_str or %BD_LVM_VDO_WRITE_POLICY_UNKNOWN if
535 : * failed to determine
536 : *
537 : * Tech category: always provided/supported
538 : */
539 2 : BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_str, GError **error) {
540 2 : if (g_strcmp0 (policy_str, "auto") == 0)
541 0 : return BD_LVM_VDO_WRITE_POLICY_AUTO;
542 2 : else if (g_strcmp0 (policy_str, "sync") == 0)
543 2 : return BD_LVM_VDO_WRITE_POLICY_SYNC;
544 0 : else if (g_strcmp0 (policy_str, "async") == 0)
545 0 : return BD_LVM_VDO_WRITE_POLICY_ASYNC;
546 : else {
547 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_VDO_POLICY_INVAL,
548 : "Invalid policy given: %s", policy_str);
549 0 : return BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
550 : }
551 : }
552 :
553 : /**
554 : * bd_lvm_vdo_get_stats_full:
555 : * @vg_name: name of the VG that contains @pool_name VDO pool
556 : * @pool_name: name of the VDO pool to get statistics for
557 : * @error: (out) (optional): place to store error (if any)
558 : *
559 : * Returns: (transfer full) (element-type utf8 utf8): hashtable of type string - string of available
560 : * statistics or %NULL in case of error
561 : * (@error gets populated in those cases)
562 : *
563 : * Statistics are collected from the values exposed by the kernel `dm-vdo` module.
564 : *
565 : * Some of the keys are computed to mimic the information produced by the vdo tools.
566 : * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version.
567 : *
568 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
569 : */
570 4 : GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error) {
571 8 : g_autofree gchar *kvdo_name = g_strdup_printf ("%s-%s-%s", vg_name, pool_name, VDO_POOL_SUFFIX);
572 4 : return vdo_get_stats_full (kvdo_name, error);
573 : }
574 :
575 : /**
576 : * bd_lvm_vdo_get_stats:
577 : * @vg_name: name of the VG that contains @pool_name VDO pool
578 : * @pool_name: name of the VDO pool to get statistics for
579 : * @error: (out) (optional): place to store error (if any)
580 : *
581 : * Returns: (transfer full): a structure containing selected statistics or %NULL in case of error
582 : * (@error gets populated in those cases)
583 : *
584 : * In contrast to @bd_lvm_vdo_get_stats_full this function will only return selected statistics
585 : * in a fixed structure. In case a value is not available, -1 would be returned.
586 : *
587 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
588 : */
589 2 : BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error) {
590 2 : GHashTable *full_stats = NULL;
591 2 : BDLVMVDOStats *stats = NULL;
592 :
593 2 : full_stats = bd_lvm_vdo_get_stats_full (vg_name, pool_name, error);
594 2 : if (!full_stats)
595 0 : return NULL;
596 :
597 2 : stats = g_new0 (BDLVMVDOStats, 1);
598 2 : get_stat_val64_default (full_stats, "blockSize", &stats->block_size, -1);
599 2 : get_stat_val64_default (full_stats, "logicalBlockSize", &stats->logical_block_size, -1);
600 2 : get_stat_val64_default (full_stats, "physicalBlocks", &stats->physical_blocks, -1);
601 2 : get_stat_val64_default (full_stats, "dataBlocksUsed", &stats->data_blocks_used, -1);
602 2 : get_stat_val64_default (full_stats, "overheadBlocksUsed", &stats->overhead_blocks_used, -1);
603 2 : get_stat_val64_default (full_stats, "logicalBlocksUsed", &stats->logical_blocks_used, -1);
604 2 : get_stat_val64_default (full_stats, "usedPercent", &stats->used_percent, -1);
605 2 : get_stat_val64_default (full_stats, "savingPercent", &stats->saving_percent, -1);
606 2 : if (!get_stat_val_double (full_stats, "writeAmplificationRatio", &stats->write_amplification_ratio))
607 0 : stats->write_amplification_ratio = -1;
608 :
609 2 : g_hash_table_destroy (full_stats);
610 :
611 2 : return stats;
612 : }
613 :
614 : /* check whether the LVM devices file is enabled by LVM
615 : * we use the existence of the "lvmdevices" command to check whether the feature is available
616 : * or not, but this can still be disabled either in LVM or in lvm.conf
617 : */
618 286 : static gboolean _lvm_devices_enabled () {
619 286 : const gchar *args[6] = {"lvmconfig", "--typeconfig", NULL, "devices/use_devicesfile", NULL, NULL};
620 286 : gboolean ret = FALSE;
621 286 : GError *loc_error = NULL;
622 286 : gchar *output = NULL;
623 286 : gboolean enabled = FALSE;
624 286 : gint scanned = 0;
625 286 : g_autofree gchar *config_arg = NULL;
626 :
627 : /* try full config first -- if we get something from this it means the feature is
628 : explicitly enabled or disabled by system lvm.conf or using the --config option */
629 286 : args[2] = "full";
630 :
631 : /* make sure to include the global config from us when getting the current config value */
632 286 : g_mutex_lock (&global_config_lock);
633 286 : if (global_config_str) {
634 10 : config_arg = g_strdup_printf ("--config=%s", global_config_str);
635 10 : args[4] = config_arg;
636 : }
637 :
638 286 : ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
639 286 : g_mutex_unlock (&global_config_lock);
640 286 : if (ret) {
641 286 : scanned = sscanf (output, "use_devicesfile=%u", &enabled);
642 286 : g_free (output);
643 286 : if (scanned != 1)
644 0 : return FALSE;
645 :
646 286 : return enabled;
647 : } else {
648 0 : g_clear_error (&loc_error);
649 0 : g_free (output);
650 : }
651 :
652 0 : output = NULL;
653 :
654 : /* now try default */
655 0 : args[2] = "default";
656 0 : ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
657 0 : if (ret) {
658 0 : scanned = sscanf (output, "# use_devicesfile=%u", &enabled);
659 0 : g_free (output);
660 0 : if (scanned != 1)
661 0 : return FALSE;
662 :
663 0 : return enabled;
664 : } else {
665 0 : g_clear_error (&loc_error);
666 0 : g_free (output);
667 : }
668 :
669 0 : return FALSE;
670 : }
671 :
672 : /**
673 : * bd_lvm_devices_add:
674 : * @device: device (PV) to add to the devices file
675 : * @devices_file: (nullable): LVM devices file or %NULL for default
676 : * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
677 : * @error: (out) (optional): place to store error (if any)
678 : *
679 : * Returns: whether the @device was successfully added to @devices_file or not
680 : *
681 : * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
682 : */
683 6 : gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
684 6 : const gchar *args[5] = {"lvmdevices", "--adddev", device, NULL, NULL};
685 6 : g_autofree gchar *devfile = NULL;
686 :
687 6 : if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
688 0 : return FALSE;
689 :
690 6 : if (!_lvm_devices_enabled ()) {
691 2 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
692 : "LVM devices file not enabled.");
693 2 : return FALSE;
694 : }
695 :
696 4 : if (devices_file) {
697 4 : devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
698 4 : args[3] = devfile;
699 : }
700 :
701 4 : return bd_utils_exec_and_report_error (args, extra, error);
702 : }
703 :
704 : /**
705 : * bd_lvm_devices_delete:
706 : * @device: device (PV) to delete from the devices file
707 : * @devices_file: (nullable): LVM devices file or %NULL for default
708 : * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
709 : * @error: (out) (optional): place to store error (if any)
710 : *
711 : * Returns: whether the @device was successfully removed from @devices_file or not
712 : *
713 : * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
714 : */
715 280 : gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
716 280 : const gchar *args[5] = {"lvmdevices", "--deldev", device, NULL, NULL};
717 280 : g_autofree gchar *devfile = NULL;
718 :
719 280 : if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
720 0 : return FALSE;
721 :
722 280 : if (!_lvm_devices_enabled ()) {
723 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
724 : "LVM devices file not enabled.");
725 0 : return FALSE;
726 : }
727 :
728 280 : if (devices_file) {
729 4 : devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
730 4 : args[3] = devfile;
731 : }
732 :
733 280 : return bd_utils_exec_and_report_error (args, extra, error);
734 : }
735 :
736 : /**
737 : * bd_lvm_config_get:
738 : * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config
739 : * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section
740 : * @type: type of the config, e.g. 'full' or 'current'
741 : * @values_only: whether to include only values without keys in the output
742 : * @global_config: whether to include our internal global config in the call or not
743 : * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command
744 : * (just passed to LVM as is)
745 : * @error: (out) (optional): place to store error (if any)
746 : *
747 : * Returns: (transfer full): Requested LVM config @section and @setting configuration or %NULL in case of error.
748 : *
749 : * Tech category: %BD_LVM_TECH_CONFIG no mode (it is ignored)
750 : */
751 18 : gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error) {
752 18 : g_autofree gchar *conf_spec = NULL;
753 18 : g_autofree gchar *config_arg = NULL;
754 18 : const gchar *args[7] = {"lvmconfig", "--typeconfig", NULL, NULL, NULL, NULL, NULL};
755 18 : guint next_arg = 2;
756 18 : gchar *output = NULL;
757 18 : gboolean success = FALSE;
758 :
759 18 : if (!section && setting) {
760 2 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
761 : "Specifying setting without section is not supported.");
762 2 : return NULL;
763 : }
764 :
765 16 : if (section)
766 14 : if (setting)
767 12 : conf_spec = g_strdup_printf ("%s/%s", section, setting);
768 : else
769 2 : conf_spec = g_strdup (section);
770 : else
771 2 : conf_spec = NULL;
772 :
773 16 : args[next_arg++] = type;
774 16 : args[next_arg++] = conf_spec;
775 16 : if (values_only)
776 14 : args[next_arg++] = "--valuesonly";
777 :
778 16 : g_mutex_lock (&global_config_lock);
779 16 : if (global_config && global_config_str) {
780 4 : config_arg = g_strdup_printf ("--config=%s", global_config_str);
781 4 : args[next_arg++] = config_arg;
782 : }
783 16 : g_mutex_unlock (&global_config_lock);
784 :
785 16 : success = bd_utils_exec_and_capture_output (args, extra, &output, error);
786 16 : if (!success)
787 0 : return NULL;
788 16 : return g_strchomp (output);
789 : }
790 :
791 8 : gboolean _vgcfgbackup_restore (const gchar *command, const gchar *vg_name, const gchar *file, const BDExtraArg **extra, GError **error) {
792 8 : const gchar *args[6] = {"lvm", NULL, NULL, NULL, NULL, NULL};
793 8 : guint next_arg = 1;
794 8 : gchar *output = NULL;
795 8 : g_autofree gchar *config_arg = NULL;
796 :
797 8 : args[next_arg++] = command;
798 8 : if (file) {
799 4 : args[next_arg++] = "-f";
800 4 : args[next_arg++] = file;
801 : }
802 8 : args[next_arg++] = vg_name;
803 :
804 8 : g_mutex_lock (&global_config_lock);
805 8 : if (global_config_str) {
806 0 : config_arg = g_strdup_printf ("--config=%s", global_config_str);
807 0 : args[next_arg++] = config_arg;
808 : }
809 8 : g_mutex_unlock (&global_config_lock);
810 :
811 8 : return bd_utils_exec_and_capture_output (args, extra, &output, error);
812 : }
813 :
814 : /**
815 : * bd_lvm_vgcfgbackup:
816 : * @vg_name: name of the VG to backup configuration
817 : * @backup_file: (nullable): file to save the backup to or %NULL for using the default backup file
818 : * in /etc/lvm/backup
819 : * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgbackup command
820 : * (just passed to LVM as is)
821 : * @error: (out) (optional): place to store error (if any)
822 : *
823 : * Note: This function does not back up the data content of LVs. See `vgcfbackup(8)` man page
824 : * for more information.
825 : *
826 : * Returns: Whether the backup was successfully created or not.
827 : *
828 : * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
829 : */
830 4 : gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) {
831 4 : return _vgcfgbackup_restore ("vgcfgbackup", vg_name, backup_file, extra, error);
832 : }
833 :
834 : /**
835 : * bd_lvm_vgcfgrestore:
836 : * @vg_name: name of the VG to restore configuration
837 : * @backup_file: (nullable): file to restore VG configuration from to or %NULL for using the
838 : * latest backup in /etc/lvm/backup
839 : * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgrestore command
840 : * (just passed to LVM as is)
841 : * @error: (out) (optional): place to store error (if any)
842 : *
843 : * Note: This function restores VG configuration created by %bd_lvm_vgcfgbackup from given
844 : * @backup_file or from the latest backup in /etc/lvm/backup.
845 : *
846 : * Returns: Whether the configuration was successfully restored or not.
847 : *
848 : * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
849 : */
850 4 : gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) {
851 4 : return _vgcfgbackup_restore ("vgcfgrestore", vg_name, backup_file, extra, error);
852 : }
853 :
854 : /**
855 : * bd_lvm_cache_stats:
856 : * @vg_name: name of the VG containing the @cached_lv
857 : * @cached_lv: cached LV to get stats for
858 : * @error: (out) (optional): place to store error (if any)
859 : *
860 : * Returns: stats for the @cached_lv or %NULL in case of error
861 : *
862 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
863 : */
864 6 : BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error) {
865 6 : struct dm_pool *pool = NULL;
866 6 : struct dm_task *task = NULL;
867 : struct dm_info info;
868 6 : struct dm_status_cache *status = NULL;
869 6 : gchar *map_name = NULL;
870 6 : guint64 start = 0;
871 6 : guint64 length = 0;
872 6 : gchar *type = NULL;
873 6 : gchar *params = NULL;
874 6 : BDLVMCacheStats *ret = NULL;
875 6 : BDLVMLVdata *lvdata = NULL;
876 :
877 6 : lvdata = bd_lvm_lvinfo (vg_name, cached_lv, error);
878 6 : if (!lvdata)
879 0 : return NULL;
880 :
881 6 : pool = dm_pool_create ("bd-pool", 20);
882 :
883 6 : if (g_strcmp0 (lvdata->segtype, "thin-pool") == 0)
884 2 : map_name = dm_build_dm_name (pool, vg_name, lvdata->data_lv, NULL);
885 : else
886 : /* translate the VG+LV name into the DM map name */
887 4 : map_name = dm_build_dm_name (pool, vg_name, cached_lv, NULL);
888 :
889 6 : bd_lvm_lvdata_free (lvdata);
890 :
891 6 : task = dm_task_create (DM_DEVICE_STATUS);
892 6 : if (!task) {
893 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
894 : "Failed to create DM task for the cache map '%s': ", map_name);
895 0 : dm_pool_destroy (pool);
896 0 : return NULL;
897 : }
898 :
899 6 : if (dm_task_set_name (task, map_name) == 0) {
900 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
901 : "Failed to create DM task for the cache map '%s': ", map_name);
902 0 : dm_task_destroy (task);
903 0 : dm_pool_destroy (pool);
904 0 : return NULL;
905 : }
906 :
907 6 : if (dm_task_run (task) == 0) {
908 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
909 : "Failed to run the DM task for the cache map '%s': ", map_name);
910 0 : dm_task_destroy (task);
911 0 : dm_pool_destroy (pool);
912 0 : return NULL;
913 : }
914 :
915 6 : if (dm_task_get_info (task, &info) == 0) {
916 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
917 : "Failed to get task info for the cache map '%s': ", map_name);
918 0 : dm_task_destroy (task);
919 0 : dm_pool_destroy (pool);
920 0 : return NULL;
921 : }
922 :
923 6 : if (!info.exists) {
924 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_NOCACHE,
925 : "The cache map '%s' doesn't exist: ", map_name);
926 0 : dm_task_destroy (task);
927 0 : dm_pool_destroy (pool);
928 0 : return NULL;
929 : }
930 :
931 6 : dm_get_next_target (task, NULL, &start, &length, &type, ¶ms);
932 :
933 6 : if (dm_get_status_cache (pool, params, &status) == 0) {
934 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
935 : "Failed to get status of the cache map '%s': ", map_name);
936 0 : dm_task_destroy (task);
937 0 : dm_pool_destroy (pool);
938 0 : return NULL;
939 : }
940 :
941 6 : ret = g_new0 (BDLVMCacheStats, 1);
942 6 : ret->block_size = status->block_size * SECTOR_SIZE;
943 6 : ret->cache_size = status->total_blocks * ret->block_size;
944 6 : ret->cache_used = status->used_blocks * ret->block_size;
945 :
946 6 : ret->md_block_size = status->metadata_block_size * SECTOR_SIZE;
947 6 : ret->md_size = status->metadata_total_blocks * ret->md_block_size;
948 6 : ret->md_used = status->metadata_used_blocks * ret->md_block_size;
949 :
950 6 : ret->read_hits = status->read_hits;
951 6 : ret->read_misses = status->read_misses;
952 6 : ret->write_hits = status->write_hits;
953 6 : ret->write_misses = status->write_misses;
954 :
955 6 : if (status->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH)
956 6 : ret->mode = BD_LVM_CACHE_MODE_WRITETHROUGH;
957 0 : else if (status->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
958 0 : ret->mode = BD_LVM_CACHE_MODE_WRITEBACK;
959 : else {
960 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
961 : "Failed to determine status of the cache from '%"G_GUINT64_FORMAT"': ",
962 0 : status->feature_flags);
963 0 : dm_task_destroy (task);
964 0 : dm_pool_destroy (pool);
965 0 : bd_lvm_cache_stats_free (ret);
966 0 : return NULL;
967 : }
968 :
969 6 : dm_task_destroy (task);
970 6 : dm_pool_destroy (pool);
971 :
972 6 : return ret;
973 : }
|