]> Git Repo - linux.git/blob - drivers/md/dm-vdo/dump.c
Merge tag 'kbuild-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[linux.git] / drivers / md / dm-vdo / dump.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2023 Red Hat
4  */
5
6 #include "dump.h"
7
8 #include <linux/module.h>
9
10 #include "memory-alloc.h"
11 #include "string-utils.h"
12
13 #include "constants.h"
14 #include "data-vio.h"
15 #include "dedupe.h"
16 #include "funnel-workqueue.h"
17 #include "io-submitter.h"
18 #include "logger.h"
19 #include "types.h"
20 #include "vdo.h"
21
22 enum dump_options {
23         /* Work queues */
24         SHOW_QUEUES,
25         /* Memory pools */
26         SHOW_VIO_POOL,
27         /* Others */
28         SHOW_VDO_STATUS,
29         /* This one means an option overrides the "default" choices, instead of altering them. */
30         SKIP_DEFAULT
31 };
32
33 enum dump_option_flags {
34         /* Work queues */
35         FLAG_SHOW_QUEUES = (1 << SHOW_QUEUES),
36         /* Memory pools */
37         FLAG_SHOW_VIO_POOL = (1 << SHOW_VIO_POOL),
38         /* Others */
39         FLAG_SHOW_VDO_STATUS = (1 << SHOW_VDO_STATUS),
40         /* Special */
41         FLAG_SKIP_DEFAULT = (1 << SKIP_DEFAULT)
42 };
43
44 #define FLAGS_ALL_POOLS (FLAG_SHOW_VIO_POOL)
45 #define DEFAULT_DUMP_FLAGS (FLAG_SHOW_QUEUES | FLAG_SHOW_VDO_STATUS)
46 /* Another static buffer... log10(256) = 2.408+, round up: */
47 #define DIGITS_PER_U64 (1 + sizeof(u64) * 2409 / 1000)
48
49 static inline bool is_arg_string(const char *arg, const char *this_option)
50 {
51         /* convention seems to be case-independent options */
52         return strncasecmp(arg, this_option, strlen(this_option)) == 0;
53 }
54
55 static void do_dump(struct vdo *vdo, unsigned int dump_options_requested,
56                     const char *why)
57 {
58         u32 active, maximum;
59         s64 outstanding;
60
61         vdo_log_info("%s dump triggered via %s", VDO_LOGGING_MODULE_NAME, why);
62         active = get_data_vio_pool_active_requests(vdo->data_vio_pool);
63         maximum = get_data_vio_pool_maximum_requests(vdo->data_vio_pool);
64         outstanding = (atomic64_read(&vdo->stats.bios_submitted) -
65                        atomic64_read(&vdo->stats.bios_completed));
66         vdo_log_info("%u device requests outstanding (max %u), %lld bio requests outstanding, device '%s'",
67                      active, maximum, outstanding,
68                      vdo_get_device_name(vdo->device_config->owning_target));
69         if (((dump_options_requested & FLAG_SHOW_QUEUES) != 0) && (vdo->threads != NULL)) {
70                 thread_id_t id;
71
72                 for (id = 0; id < vdo->thread_config.thread_count; id++)
73                         vdo_dump_work_queue(vdo->threads[id].queue);
74         }
75
76         vdo_dump_hash_zones(vdo->hash_zones);
77         dump_data_vio_pool(vdo->data_vio_pool,
78                            (dump_options_requested & FLAG_SHOW_VIO_POOL) != 0);
79         if ((dump_options_requested & FLAG_SHOW_VDO_STATUS) != 0)
80                 vdo_dump_status(vdo);
81
82         vdo_report_memory_usage();
83         vdo_log_info("end of %s dump", VDO_LOGGING_MODULE_NAME);
84 }
85
86 static int parse_dump_options(unsigned int argc, char *const *argv,
87                               unsigned int *dump_options_requested_ptr)
88 {
89         unsigned int dump_options_requested = 0;
90
91         static const struct {
92                 const char *name;
93                 unsigned int flags;
94         } option_names[] = {
95                 { "viopool", FLAG_SKIP_DEFAULT | FLAG_SHOW_VIO_POOL },
96                 { "vdo", FLAG_SKIP_DEFAULT | FLAG_SHOW_VDO_STATUS },
97                 { "pools", FLAG_SKIP_DEFAULT | FLAGS_ALL_POOLS },
98                 { "queues", FLAG_SKIP_DEFAULT | FLAG_SHOW_QUEUES },
99                 { "threads", FLAG_SKIP_DEFAULT | FLAG_SHOW_QUEUES },
100                 { "default", FLAG_SKIP_DEFAULT | DEFAULT_DUMP_FLAGS },
101                 { "all", ~0 },
102         };
103
104         bool options_okay = true;
105         unsigned int i;
106
107         for (i = 1; i < argc; i++) {
108                 unsigned int j;
109
110                 for (j = 0; j < ARRAY_SIZE(option_names); j++) {
111                         if (is_arg_string(argv[i], option_names[j].name)) {
112                                 dump_options_requested |= option_names[j].flags;
113                                 break;
114                         }
115                 }
116                 if (j == ARRAY_SIZE(option_names)) {
117                         vdo_log_warning("dump option name '%s' unknown", argv[i]);
118                         options_okay = false;
119                 }
120         }
121         if (!options_okay)
122                 return -EINVAL;
123         if ((dump_options_requested & FLAG_SKIP_DEFAULT) == 0)
124                 dump_options_requested |= DEFAULT_DUMP_FLAGS;
125         *dump_options_requested_ptr = dump_options_requested;
126         return 0;
127 }
128
129 /* Dump as specified by zero or more string arguments. */
130 int vdo_dump(struct vdo *vdo, unsigned int argc, char *const *argv, const char *why)
131 {
132         unsigned int dump_options_requested = 0;
133         int result = parse_dump_options(argc, argv, &dump_options_requested);
134
135         if (result != 0)
136                 return result;
137
138         do_dump(vdo, dump_options_requested, why);
139         return 0;
140 }
141
142 /* Dump everything we know how to dump */
143 void vdo_dump_all(struct vdo *vdo, const char *why)
144 {
145         do_dump(vdo, ~0, why);
146 }
147
148 /*
149  * Dump out the data_vio waiters on a waitq.
150  * wait_on should be the label to print for queue (e.g. logical or physical)
151  */
152 static void dump_vio_waiters(struct vdo_wait_queue *waitq, char *wait_on)
153 {
154         struct vdo_waiter *waiter, *first = vdo_waitq_get_first_waiter(waitq);
155         struct data_vio *data_vio;
156
157         if (first == NULL)
158                 return;
159
160         data_vio = vdo_waiter_as_data_vio(first);
161
162         vdo_log_info("      %s is locked. Waited on by: vio %px pbn %llu lbn %llu d-pbn %llu lastOp %s",
163                      wait_on, data_vio, data_vio->allocation.pbn, data_vio->logical.lbn,
164                      data_vio->duplicate.pbn, get_data_vio_operation_name(data_vio));
165
166         for (waiter = first->next_waiter; waiter != first; waiter = waiter->next_waiter) {
167                 data_vio = vdo_waiter_as_data_vio(waiter);
168                 vdo_log_info("     ... and : vio %px pbn %llu lbn %llu d-pbn %llu lastOp %s",
169                              data_vio, data_vio->allocation.pbn, data_vio->logical.lbn,
170                              data_vio->duplicate.pbn,
171                              get_data_vio_operation_name(data_vio));
172         }
173 }
174
175 /*
176  * Encode various attributes of a data_vio as a string of one-character flags. This encoding is for
177  * logging brevity:
178  *
179  * R => vio completion result not VDO_SUCCESS
180  * W => vio is on a waitq
181  * D => vio is a duplicate
182  * p => vio is a partial block operation
183  * z => vio is a zero block
184  * d => vio is a discard
185  *
186  * The common case of no flags set will result in an empty, null-terminated buffer. If any flags
187  * are encoded, the first character in the string will be a space character.
188  */
189 static void encode_vio_dump_flags(struct data_vio *data_vio, char buffer[8])
190 {
191         char *p_flag = buffer;
192         *p_flag++ = ' ';
193         if (data_vio->vio.completion.result != VDO_SUCCESS)
194                 *p_flag++ = 'R';
195         if (data_vio->waiter.next_waiter != NULL)
196                 *p_flag++ = 'W';
197         if (data_vio->is_duplicate)
198                 *p_flag++ = 'D';
199         if (data_vio->is_partial)
200                 *p_flag++ = 'p';
201         if (data_vio->is_zero)
202                 *p_flag++ = 'z';
203         if (data_vio->remaining_discard > 0)
204                 *p_flag++ = 'd';
205         if (p_flag == &buffer[1]) {
206                 /* No flags, so remove the blank space. */
207                 p_flag = buffer;
208         }
209         *p_flag = '\0';
210 }
211
212 /* Implements buffer_dump_function. */
213 void dump_data_vio(void *data)
214 {
215         struct data_vio *data_vio = data;
216
217         /*
218          * This just needs to be big enough to hold a queue (thread) name and a function name (plus
219          * a separator character and NUL). The latter is limited only by taste.
220          *
221          * In making this static, we're assuming only one "dump" will run at a time. If more than
222          * one does run, the log output will be garbled anyway.
223          */
224         static char vio_completion_dump_buffer[100 + MAX_VDO_WORK_QUEUE_NAME_LEN];
225         static char vio_block_number_dump_buffer[sizeof("P L D") + 3 * DIGITS_PER_U64];
226         static char vio_flush_generation_buffer[sizeof(" FG") + DIGITS_PER_U64];
227         static char flags_dump_buffer[8];
228
229         /*
230          * We're likely to be logging a couple thousand of these lines, and in some circumstances
231          * syslogd may have trouble keeping up, so keep it BRIEF rather than user-friendly.
232          */
233         vdo_dump_completion_to_buffer(&data_vio->vio.completion,
234                                       vio_completion_dump_buffer,
235                                       sizeof(vio_completion_dump_buffer));
236         if (data_vio->is_duplicate) {
237                 snprintf(vio_block_number_dump_buffer,
238                          sizeof(vio_block_number_dump_buffer), "P%llu L%llu D%llu",
239                          data_vio->allocation.pbn, data_vio->logical.lbn,
240                          data_vio->duplicate.pbn);
241         } else if (data_vio_has_allocation(data_vio)) {
242                 snprintf(vio_block_number_dump_buffer,
243                          sizeof(vio_block_number_dump_buffer), "P%llu L%llu",
244                          data_vio->allocation.pbn, data_vio->logical.lbn);
245         } else {
246                 snprintf(vio_block_number_dump_buffer,
247                          sizeof(vio_block_number_dump_buffer), "L%llu",
248                          data_vio->logical.lbn);
249         }
250
251         if (data_vio->flush_generation != 0) {
252                 snprintf(vio_flush_generation_buffer,
253                          sizeof(vio_flush_generation_buffer), " FG%llu",
254                          data_vio->flush_generation);
255         } else {
256                 vio_flush_generation_buffer[0] = 0;
257         }
258
259         encode_vio_dump_flags(data_vio, flags_dump_buffer);
260
261         vdo_log_info("  vio %px %s%s %s %s%s", data_vio,
262                      vio_block_number_dump_buffer,
263                      vio_flush_generation_buffer,
264                      get_data_vio_operation_name(data_vio),
265                      vio_completion_dump_buffer,
266                      flags_dump_buffer);
267         /*
268          * might want info on: wantUDSAnswer / operation / status
269          * might want info on: bio / bios_merged
270          */
271
272         dump_vio_waiters(&data_vio->logical.waiters, "lbn");
273
274         /* might want to dump more info from vio here */
275 }
This page took 0.050505 seconds and 4 git commands to generate.