dump.c 8.3 KB

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