File Index
Files and directories
Sign in
FILE

dump.c

File preview with syntax highlighting.

linux-6.19.14/drivers/md/dm-vdo/dump.c 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
 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_DEFAULT
 31};
 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 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 */
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 (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}
~/ Read-only file preview.