Line data Source code
1 : /*
2 : * Copyright (C) 2020 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 : * Author: Vojtech Trefny <vtrefny@redhat.com>
18 : */
19 :
20 : #include <glib.h>
21 : #include <blockdev/utils.h>
22 : #include <libdevmapper.h>
23 : #include <yaml.h>
24 :
25 : #include "vdo_stats.h"
26 : #include "lvm.h"
27 :
28 :
29 : G_GNUC_INTERNAL gboolean
30 64 : get_stat_val64 (GHashTable *stats, const gchar *key, gint64 *val) {
31 : const gchar *s;
32 64 : gchar *endptr = NULL;
33 :
34 64 : s = g_hash_table_lookup (stats, key);
35 64 : if (s == NULL)
36 4 : return FALSE;
37 :
38 60 : *val = g_ascii_strtoll (s, &endptr, 0);
39 60 : if (endptr == NULL || *endptr != '\0')
40 0 : return FALSE;
41 :
42 60 : return TRUE;
43 : }
44 :
45 : G_GNUC_INTERNAL gboolean
46 16 : get_stat_val64_default (GHashTable *stats, const gchar *key, gint64 *val, gint64 def) {
47 16 : if (!get_stat_val64 (stats, key, val))
48 0 : *val = def;
49 16 : return TRUE;
50 : }
51 :
52 : G_GNUC_INTERNAL gboolean
53 2 : get_stat_val_double (GHashTable *stats, const gchar *key, gdouble *val) {
54 : const gchar *s;
55 2 : gchar *endptr = NULL;
56 :
57 2 : s = g_hash_table_lookup (stats, key);
58 2 : if (s == NULL)
59 0 : return FALSE;
60 :
61 2 : *val = g_ascii_strtod (s, &endptr);
62 2 : if (endptr == NULL || *endptr != '\0')
63 0 : return FALSE;
64 :
65 2 : return TRUE;
66 : }
67 :
68 4 : static void add_write_ampl_r_stats (GHashTable *stats) {
69 : gint64 bios_meta_write, bios_out_write, bios_in_write;
70 :
71 8 : if (! get_stat_val64 (stats, "biosMetaWrite", &bios_meta_write) ||
72 8 : ! get_stat_val64 (stats, "biosOutWrite", &bios_out_write) ||
73 4 : ! get_stat_val64 (stats, "biosInWrite", &bios_in_write))
74 0 : return;
75 :
76 4 : if (bios_in_write <= 0)
77 4 : g_hash_table_replace (stats, g_strdup ("writeAmplificationRatio"), g_strdup ("0.00"));
78 : else
79 0 : g_hash_table_replace (stats,
80 0 : g_strdup ("writeAmplificationRatio"),
81 0 : g_strdup_printf ("%.2f", (gfloat) (bios_meta_write + bios_out_write) / (gfloat) bios_in_write));
82 : }
83 :
84 4 : static void add_block_stats (GHashTable *stats) {
85 : gint64 physical_blocks, block_size, data_blocks_used, overhead_blocks_used, logical_blocks_used;
86 : gint64 savings;
87 :
88 8 : if (! get_stat_val64 (stats, "physicalBlocks", &physical_blocks) ||
89 8 : ! get_stat_val64 (stats, "blockSize", &block_size) ||
90 8 : ! get_stat_val64 (stats, "dataBlocksUsed", &data_blocks_used) ||
91 8 : ! get_stat_val64 (stats, "overheadBlocksUsed", &overhead_blocks_used) ||
92 4 : ! get_stat_val64 (stats, "logicalBlocksUsed", &logical_blocks_used))
93 0 : return;
94 :
95 8 : g_hash_table_replace (stats, g_strdup ("oneKBlocks"), g_strdup_printf ("%"G_GINT64_FORMAT, physical_blocks * block_size / 1024));
96 8 : g_hash_table_replace (stats, g_strdup ("oneKBlocksUsed"), g_strdup_printf ("%"G_GINT64_FORMAT, (data_blocks_used + overhead_blocks_used) * block_size / 1024));
97 8 : g_hash_table_replace (stats, g_strdup ("oneKBlocksAvailable"), g_strdup_printf ("%"G_GINT64_FORMAT, (physical_blocks - data_blocks_used - overhead_blocks_used) * block_size / 1024));
98 8 : g_hash_table_replace (stats, g_strdup ("usedPercent"), g_strdup_printf ("%.0f", 100.0 * (gfloat) (data_blocks_used + overhead_blocks_used) / (gfloat) physical_blocks + 0.5));
99 4 : savings = (logical_blocks_used > 0) ? (gint64) (100.0 * (gfloat) (logical_blocks_used - data_blocks_used) / (gfloat) logical_blocks_used) : 100;
100 8 : g_hash_table_replace (stats, g_strdup ("savings"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
101 4 : if (savings >= 0)
102 8 : g_hash_table_replace (stats, g_strdup ("savingPercent"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
103 : }
104 :
105 4 : static void add_journal_stats (GHashTable *stats) {
106 : gint64 journal_entries_committed, journal_entries_started, journal_entries_written;
107 : gint64 journal_blocks_committed, journal_blocks_started, journal_blocks_written;
108 :
109 8 : if (! get_stat_val64 (stats, "journalEntriesCommitted", &journal_entries_committed) ||
110 8 : ! get_stat_val64 (stats, "journalEntriesStarted", &journal_entries_started) ||
111 8 : ! get_stat_val64 (stats, "journalEntriesWritten", &journal_entries_written) ||
112 4 : ! get_stat_val64 (stats, "journalBlocksCommitted", &journal_blocks_committed) ||
113 0 : ! get_stat_val64 (stats, "journalBlocksStarted", &journal_blocks_started) ||
114 0 : ! get_stat_val64 (stats, "journalBlocksWritten", &journal_blocks_written))
115 4 : return;
116 :
117 0 : g_hash_table_replace (stats, g_strdup ("journalEntriesBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_started - journal_entries_written));
118 0 : g_hash_table_replace (stats, g_strdup ("journalEntriesWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_written - journal_entries_committed));
119 0 : g_hash_table_replace (stats, g_strdup ("journalBlocksBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_started - journal_blocks_written));
120 0 : g_hash_table_replace (stats, g_strdup ("journalBlocksWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_written - journal_blocks_committed));
121 : }
122 :
123 4 : static void add_computed_stats (GHashTable *stats) {
124 : const gchar *s;
125 :
126 4 : s = g_hash_table_lookup (stats, "logicalBlockSize");
127 4 : g_hash_table_replace (stats,
128 4 : g_strdup ("fiveTwelveByteEmulation"),
129 8 : g_strdup ((g_strcmp0 (s, "512") == 0) ? "true" : "false"));
130 :
131 4 : add_write_ampl_r_stats (stats);
132 4 : add_block_stats (stats);
133 4 : add_journal_stats (stats);
134 4 : }
135 :
136 : enum parse_flags {
137 : PARSE_NEXT_KEY,
138 : PARSE_NEXT_VAL,
139 : PARSE_NEXT_IGN,
140 : };
141 :
142 : G_GNUC_INTERNAL GHashTable *
143 4 : vdo_get_stats_full (const gchar *name, GError **error) {
144 4 : struct dm_task *dmt = NULL;
145 4 : const gchar *response = NULL;
146 : yaml_parser_t parser;
147 : yaml_token_t token;
148 4 : GHashTable *stats = NULL;
149 4 : gchar *key = NULL;
150 4 : gsize len = 0;
151 4 : int next_token = PARSE_NEXT_IGN;
152 4 : gchar *prefix = NULL;
153 :
154 4 : dmt = dm_task_create (DM_DEVICE_TARGET_MSG);
155 4 : if (!dmt) {
156 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
157 : "Failed to create DM task");
158 0 : return NULL;
159 : }
160 :
161 4 : if (!dm_task_set_name (dmt, name)) {
162 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
163 : "Failed to set name for DM task");
164 0 : dm_task_destroy (dmt);
165 0 : return NULL;
166 : }
167 :
168 4 : if (!dm_task_set_message (dmt, "stats")) {
169 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
170 : "Failed to set message for DM task");
171 0 : dm_task_destroy (dmt);
172 0 : return NULL;
173 : }
174 :
175 4 : if (!dm_task_run (dmt)) {
176 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
177 : "Failed to run DM task");
178 0 : dm_task_destroy (dmt);
179 0 : return NULL;
180 : }
181 :
182 4 : response = dm_task_get_message_response (dmt);
183 4 : if (!response) {
184 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
185 : "Failed to get response from the DM task");
186 0 : dm_task_destroy (dmt);
187 0 : return NULL;
188 : }
189 :
190 4 : if (!yaml_parser_initialize (&parser)) {
191 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
192 : "Failed to get initialize YAML parser");
193 0 : dm_task_destroy (dmt);
194 0 : return NULL;
195 : }
196 :
197 4 : stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
198 4 : yaml_parser_set_input_string (&parser, (guchar *) response, strlen (response));
199 :
200 : do {
201 3760 : yaml_parser_scan (&parser, &token);
202 3760 : switch (token.type) {
203 : /* key */
204 728 : case YAML_KEY_TOKEN:
205 728 : next_token = PARSE_NEXT_KEY;
206 728 : break;
207 : /* value */
208 728 : case YAML_VALUE_TOKEN:
209 728 : next_token = PARSE_NEXT_VAL;
210 728 : break;
211 : /* block mapping */
212 0 : case YAML_BLOCK_MAPPING_START_TOKEN:
213 0 : if (next_token == PARSE_NEXT_VAL)
214 : /* we were expecting to read a key-value pair but this is actually
215 : a block start, so we need to free the key we're not going to use */
216 0 : g_free (key);
217 0 : break;
218 : /* mapping */
219 108 : case YAML_FLOW_MAPPING_START_TOKEN:
220 : /* start of flow mapping -> previously read key will be used as prefix
221 : for all keys in the mapping:
222 : previous key: biosInProgress
223 : keys in the mapping: Read, Write...
224 : with prefix: biosInProgressRead, biosInProgressWrite...
225 : */
226 108 : prefix = key;
227 108 : break;
228 108 : case YAML_FLOW_MAPPING_END_TOKEN:
229 : /* end of flow mapping, discard the prefix used */
230 108 : g_free (prefix);
231 108 : prefix = NULL;
232 108 : break;
233 : /* actual data */
234 1352 : case YAML_SCALAR_TOKEN:
235 1352 : if (next_token == PARSE_NEXT_KEY) {
236 728 : if (prefix) {
237 552 : key = g_strdup_printf ("%s%s", prefix, (const gchar *) token.data.scalar.value);
238 552 : len = strlen (prefix);
239 : /* make sure the key with the prefix is still camelCase */
240 552 : key[len] = g_ascii_toupper (key[len]);
241 : } else
242 352 : key = g_strdup ((const gchar *) token.data.scalar.value);
243 624 : } else if (next_token == PARSE_NEXT_VAL) {
244 624 : gchar *val = g_strdup ((const gchar *) token.data.scalar.value);
245 624 : g_hash_table_insert (stats, key, val);
246 : }
247 1352 : break;
248 736 : default:
249 736 : break;
250 : }
251 :
252 3760 : if (token.type != YAML_STREAM_END_TOKEN)
253 3756 : yaml_token_delete (&token);
254 3760 : } while (token.type != YAML_STREAM_END_TOKEN);
255 :
256 4 : yaml_parser_delete (&parser);
257 4 : dm_task_destroy (dmt);
258 :
259 4 : if (stats != NULL)
260 4 : add_computed_stats (stats);
261 :
262 4 : return stats;
263 : }
|