1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2023 Red Hat
8 #include <linux/module.h>
10 #include "memory-alloc.h"
11 #include "string-utils.h"
13 #include "constants.h"
16 #include "funnel-workqueue.h"
17 #include "io-submitter.h"
29 /* This one means an option overrides the "default" choices, instead of altering them. */
33 enum dump_option_flags
{
35 FLAG_SHOW_QUEUES
= (1 << SHOW_QUEUES
),
37 FLAG_SHOW_VIO_POOL
= (1 << SHOW_VIO_POOL
),
39 FLAG_SHOW_VDO_STATUS
= (1 << SHOW_VDO_STATUS
),
41 FLAG_SKIP_DEFAULT
= (1 << SKIP_DEFAULT
)
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)
49 static inline bool is_arg_string(const char *arg
, const char *this_option
)
51 /* convention seems to be case-independent options */
52 return strncasecmp(arg
, this_option
, strlen(this_option
)) == 0;
55 static void do_dump(struct vdo
*vdo
, unsigned int dump_options_requested
,
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
)) {
72 for (id
= 0; id
< vdo
->thread_config
.thread_count
; id
++)
73 vdo_dump_work_queue(vdo
->threads
[id
].queue
);
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)
82 vdo_report_memory_usage();
83 vdo_log_info("end of %s dump", VDO_LOGGING_MODULE_NAME
);
86 static int parse_dump_options(unsigned int argc
, char *const *argv
,
87 unsigned int *dump_options_requested_ptr
)
89 unsigned int dump_options_requested
= 0;
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
},
104 bool options_okay
= true;
107 for (i
= 1; i
< argc
; i
++) {
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
;
116 if (j
== ARRAY_SIZE(option_names
)) {
117 vdo_log_warning("dump option name '%s' unknown", argv
[i
]);
118 options_okay
= false;
123 if ((dump_options_requested
& FLAG_SKIP_DEFAULT
) == 0)
124 dump_options_requested
|= DEFAULT_DUMP_FLAGS
;
125 *dump_options_requested_ptr
= dump_options_requested
;
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
)
132 unsigned int dump_options_requested
= 0;
133 int result
= parse_dump_options(argc
, argv
, &dump_options_requested
);
138 do_dump(vdo
, dump_options_requested
, why
);
142 /* Dump everything we know how to dump */
143 void vdo_dump_all(struct vdo
*vdo
, const char *why
)
145 do_dump(vdo
, ~0, why
);
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)
152 static void dump_vio_waiters(struct vdo_wait_queue
*waitq
, char *wait_on
)
154 struct vdo_waiter
*waiter
, *first
= vdo_waitq_get_first_waiter(waitq
);
155 struct data_vio
*data_vio
;
160 data_vio
= vdo_waiter_as_data_vio(first
);
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
));
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
));
176 * Encode various attributes of a data_vio as a string of one-character flags. This encoding is for
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
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.
189 static void encode_vio_dump_flags(struct data_vio
*data_vio
, char buffer
[8])
191 char *p_flag
= buffer
;
193 if (data_vio
->vio
.completion
.result
!= VDO_SUCCESS
)
195 if (data_vio
->waiter
.next_waiter
!= NULL
)
197 if (data_vio
->is_duplicate
)
199 if (data_vio
->is_partial
)
201 if (data_vio
->is_zero
)
203 if (data_vio
->remaining_discard
> 0)
205 if (p_flag
== &buffer
[1]) {
206 /* No flags, so remove the blank space. */
212 /* Implements buffer_dump_function. */
213 void dump_data_vio(void *data
)
215 struct data_vio
*data_vio
= data
;
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.
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.
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];
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.
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
);
246 snprintf(vio_block_number_dump_buffer
,
247 sizeof(vio_block_number_dump_buffer
), "L%llu",
248 data_vio
->logical
.lbn
);
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
);
256 vio_flush_generation_buffer
[0] = 0;
259 encode_vio_dump_flags(data_vio
, flags_dump_buffer
);
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
,
268 * might want info on: wantUDSAnswer / operation / status
269 * might want info on: bio / bios_merged
272 dump_vio_waiters(&data_vio
->logical
.waiters
, "lbn");
274 /* might want to dump more info from vio here */