276 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2023 Red Hat4 */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 22enum 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_DEFAULT31};32 33enum 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 49static 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 55static 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 86static 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. */130int 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 */143void 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 */152static 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 for177 * logging brevity:178 *179 * R => vio completion result not VDO_SUCCESS180 * W => vio is on a waitq181 * D => vio is a duplicate182 * p => vio is a partial block operation183 * z => vio is a zero block184 * d => vio is a discard185 *186 * The common case of no flags set will result in an empty, null-terminated buffer. If any flags187 * are encoded, the first character in the string will be a space character.188 */189static 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. */213void 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 (plus219 * 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 than222 * 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 circumstances231 * 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 / status269 * might want info on: bio / bios_merged270 */271 272 dump_vio_waiters(&data_vio->logical.waiters, "lbn");273 274 /* might want to dump more info from vio here */275}276