Patch adds new 'sbitmapq' command. This command dumps the contents of the sbitmap_queue structure and the used bits in the bitmap. Also, it shows the dump of a structure array associated with the sbitmap_queue. v1 -> v2: - Update the help page (Lianbo) - Use crash interfaces (offset_table, size_table, GETBUF() and etc.) for reduce the number of readmem() (Kazu) v2 -> v3 - Fix of typos and unused variables - Update the help for follow to the crash style - Add dump of OFFSET and SIZE in symbols.c - Some code improvements Signed-off-by: Sergey Samoylenko <s.samoylenko@xxxxxxxxx> --- Makefile | 7 +- defs.h | 59 +++++ global_data.c | 1 + help.c | 103 ++++++++ sbitmap.c | 653 ++++++++++++++++++++++++++++++++++++++++++++++++++ symbols.c | 39 +++ 6 files changed, 860 insertions(+), 2 deletions(-) create mode 100644 sbitmap.c diff --git a/Makefile b/Makefile index 4fd8b78..a381c5f 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \ xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \ xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c \ ramdump.c vmware_vmss.c vmware_guestdump.c \ - xen_dom0.c kaslr_helper.c + xen_dom0.c kaslr_helper.c sbitmap.c SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \ ${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \ @@ -92,7 +92,7 @@ OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \ xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \ xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o \ ramdump.o vmware_vmss.o vmware_guestdump.o \ - xen_dom0.o kaslr_helper.o + xen_dom0.o kaslr_helper.o sbitmap.o MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README @@ -341,6 +341,9 @@ cmdline.o: ${GENERIC_HFILES} cmdline.c tools.o: ${GENERIC_HFILES} tools.c ${CC} -c ${CRASH_CFLAGS} tools.c ${WARNING_OPTIONS} ${WARNING_ERROR} +sbitmap.o: ${GENERIC_HFILES} sbitmap.c + ${CC} -c ${CRASH_CFLAGS} sbitmap.c ${WARNING_OPTIONS} ${WARNING_ERROR} + global_data.o: ${GENERIC_HFILES} global_data.c ${CC} -c ${CRASH_CFLAGS} global_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} diff --git a/defs.h b/defs.h index b63741c..d407025 100644 --- a/defs.h +++ b/defs.h @@ -18,6 +18,7 @@ #ifndef GDB_COMMON +#include <stdbool.h> #include <stdio.h> #include <stdarg.h> #include <stdint.h> @@ -2146,6 +2147,23 @@ struct offset_table { /* stash of commonly-used offsets */ long wait_queue_entry_private; long wait_queue_head_head; long wait_queue_entry_entry; + long sbitmap_word_depth; + long sbitmap_word_word; + long sbitmap_word_cleared; + long sbitmap_depth; + long sbitmap_shift; + long sbitmap_map_nr; + long sbitmap_map; + long sbitmap_queue_sb; + long sbitmap_queue_alloc_hint; + long sbitmap_queue_wake_batch; + long sbitmap_queue_wake_index; + long sbitmap_queue_ws; + long sbitmap_queue_ws_active; + long sbitmap_queue_round_robin; + long sbitmap_queue_min_shallow_depth; + long sbq_wait_state_wait_cnt; + long sbq_wait_state_wait; }; struct size_table { /* stash of commonly-used sizes */ @@ -2310,6 +2328,10 @@ struct size_table { /* stash of commonly-used sizes */ long prb_desc; long wait_queue_entry; long task_struct_state; + long sbitmap_word; + long sbitmap; + long sbitmap_queue; + long sbq_wait_state; }; struct array_table { @@ -2436,6 +2458,7 @@ DEF_LOADER(ushort); DEF_LOADER(short); typedef void *pointer_t; DEF_LOADER(pointer_t); +DEF_LOADER(bool); #define LOADER(TYPE) load_##TYPE @@ -2449,6 +2472,7 @@ DEF_LOADER(pointer_t); #define SHORT(ADDR) LOADER(short) ((char *)(ADDR)) #define UCHAR(ADDR) *((unsigned char *)((char *)(ADDR))) #define VOID_PTR(ADDR) ((void *) (LOADER(pointer_t) ((char *)(ADDR)))) +#define BOOL(ADDR) LOADER(bool) ((char *)(ADDR))) #else @@ -2462,6 +2486,7 @@ DEF_LOADER(pointer_t); #define SHORT(ADDR) *((short *)((char *)(ADDR))) #define UCHAR(ADDR) *((unsigned char *)((char *)(ADDR))) #define VOID_PTR(ADDR) *((void **)((char *)(ADDR))) +#define BOOL(ADDR) *((bool *)((char *)(ADDR))) #endif /* NEED_ALIGNED_MEM_ACCESS */ @@ -4962,6 +4987,7 @@ void cmd_mach(void); /* main.c */ void cmd_help(void); /* help.c */ void cmd_test(void); /* test.c */ void cmd_ascii(void); /* tools.c */ +void cmd_sbitmapq(void); /* sbitmap.c */ void cmd_bpf(void); /* bfp.c */ void cmd_set(void); /* tools.c */ void cmd_eval(void); /* tools.c */ @@ -5575,6 +5601,7 @@ extern char *help_rd[]; extern char *help_repeat[]; extern char *help_runq[]; extern char *help_ipcs[]; +extern char *help_sbitmapq[]; extern char *help_search[]; extern char *help_set[]; extern char *help_sig[]; @@ -5844,6 +5871,38 @@ void devdump_info(void *, ulonglong, FILE *); void ipcs_init(void); ulong idr_find(ulong, int); +/* + * sbitmap.c + */ +/* sbitmap helpers */ +struct sbitmap_context { + unsigned depth; + unsigned shift; + unsigned map_nr; + ulong map_addr; +}; + +typedef bool (*sbitmap_for_each_fn)(unsigned int idx, void *p); + +void sbitmap_for_each_set(const struct sbitmap_context *sc, + sbitmap_for_each_fn fn, void *data); +void sbitmap_context_load(ulong addr, struct sbitmap_context *sc); + +/* sbitmap_queue helpers */ +typedef bool (*sbitmapq_for_each_fn)(unsigned int idx, ulong addr, void *p); + +struct sbitmapq_ops { + /* array params associated with the bitmap */ + ulong addr; + ulong size; + /* callback params */ + sbitmapq_for_each_fn fn; + void *p; +}; + +void sbitmapq_init(void); +void sbitmapq_for_each_set(ulong addr, struct sbitmapq_ops *ops); + #ifdef ARM void arm_init(int); void arm_dump_machdep_table(ulong); diff --git a/global_data.c b/global_data.c index a316d1c..f9bb7d0 100644 --- a/global_data.c +++ b/global_data.c @@ -105,6 +105,7 @@ struct command_table_entry linux_command_table[] = { {"rd", cmd_rd, help_rd, MINIMAL}, {"repeat", cmd_repeat, help_repeat, 0}, {"runq", cmd_runq, help_runq, REFRESH_TASK_TABLE}, + {"sbitmapq", cmd_sbitmapq, help_sbitmapq, 0}, {"search", cmd_search, help_search, 0}, {"set", cmd_set, help_set, REFRESH_TASK_TABLE | MINIMAL}, {"sig", cmd_sig, help_sig, REFRESH_TASK_TABLE}, diff --git a/help.c b/help.c index 04a7eff..82defda 100644 --- a/help.c +++ b/help.c @@ -962,6 +962,109 @@ char *help_ascii[] = { NULL }; +char *help_sbitmapq[] = { +"sbitmapq", +"sbitmap_queue struct contents", +"[-s struct[.member[,member]] -a address [-v]] -[x|d] address", +" The command dumps the contents of the sbitmap_queue structure and", +" the used bits in the bitmap. Also, it shows the dump of a structure", +" array associated with the sbitmap_queue.", +"", +" The arguments are as follows:", +"", +" -s struct - name of a C-code structure, that is stored in an array", +" associated with sbitmap_queue structure. Use the", +" \"struct.member\" format in order to display a particular", +" member of the structure. -s option requires -a option", +" -a address - address of a structure array associated with sbitmap_queue", +" structure. The set bits in sbitmap are used for the index", +" in an associated array.", +" -x - override default output format with hexadecimal format.", +" -d - override default output format with decimal format.", +" -v - By default, the sbitmap command shows only a used sbitmap", +" index and a structure address in the associated array.", +" This flag says to print a formatted display of the", +" contents of a structure in an associated array. -v option", +" requires of -s.", +"", +"EXAMPLES", +"", +" All examples are shown on the base of Linux Target system with iSCSI", +" transport.", +"", +" Display the common sbitmap information for target session:", +"", +" %s> struct -oh se_session 0xc0000000e118c760 | grep sbitmap_queue", +" [c0000000e118c808] struct sbitmap_queue sess_tag_pool;", +" %s>", +" %s> sbitmapq c0000000e118c808", +" depth = 136", +" busy = 4", +" cleared = 26", +" bits_per_word = 32", +" map_nr = 5", +" alloc_hint = {74, 36, 123, 101}", +" wake_batch = 8", +" wake_index = 0", +" ws_active = 0", +" ws = {", +" { .wait_cnt = 8, .wait = inactive },", +" { .wait_cnt = 8, .wait = inactive },", +" { .wait_cnt = 8, .wait = inactive },", +" { .wait_cnt = 8, .wait = inactive },", +" { .wait_cnt = 8, .wait = inactive },", +" { .wait_cnt = 8, .wait = inactive },", +" { .wait_cnt = 8, .wait = inactive },", +" { .wait_cnt = 8, .wait = inactive },", +" }", +" round_robin = 0", +" min_shallow_depth = 4294967295", +"", +" 00000000: 0000 0000 0000 0000 0030 0000 0000 0000", +" 00000010: 00", +"", +" Display the addresses of structure are associated with", +" sbitmap_queue (for iscsi it is 'iscsi_cmd' structure):", +"", +" %s> struct se_session 0xc0000000e118c760 | grep sess_cmd_map", +" sess_cmd_map = 0xc0000000671c0000,", +" %s>", +" %s> sbitmapq -s iscsi_cmd -a 0xc0000000671c0000 c0000000e118c808", +" 76: 0xc0000000671d5600", +" 77: 0xc0000000671d5a80", +"", +" Dump of formatted content of structures:", +"", +" %s> sbitmapq -s iscsi_cmd -a 0xc0000000671c0000 -v c0000000e118c808", +" 76 (0xc0000000671d5600):", +" struct iscsi_cmd {", +" dataout_timer_flags = 0,", +" dataout_timeout_retries = 0 '\\000',", +" error_recovery_count = 0 '\\000',", +" deferred_i_state = ISTATE_NO_STATE,", +" i_state = ISTATE_SENT_STATUS,", +" ...", +" first_data_sg = 0xc0000000e306b080,", +" first_data_sg_off = 0,", +" kmapped_nents = 1,", +" sense_reason = 0", +" }", +" 77 (0xc0000000671d5a80):", +" struct iscsi_cmd {", +" dataout_timer_flags = 0,", +" dataout_timeout_retries = 0 '\\000',", +" error_recovery_count = 0 '\\000',", +" deferred_i_state = ISTATE_NO_STATE,", +" i_state = ISTATE_NEW_CMD,", +" ...", +" first_data_sg = 0x0,", +" first_data_sg_off = 0,", +" kmapped_nents = 0,", +" sense_reason = 0", +" }", +NULL +}; + char *help_quit[] = { "quit", "exit this session", diff --git a/sbitmap.c b/sbitmap.c new file mode 100644 index 0000000..4d8feb9 --- /dev/null +++ b/sbitmap.c @@ -0,0 +1,653 @@ +/* sbitmap.c + * + * Copyright (C) 2022 YADRO. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "defs.h" + +#define SBQ_WAIT_QUEUES 8 + +/* sbitmap_queue struct context */ +struct sbitmap_queue_context { + ulong sb_addr; + ulong alloc_hint; + unsigned int wake_batch; + int wake_index; + ulong ws_addr; + int ws_active; + bool round_robin; + unsigned int min_shallow_depth; + +}; + +struct sbitmapq_data { +#define SBITMAPQ_DATA_FLAG_STRUCT_NAME (VERBOSE << 1) +#define SBITMAPQ_DATA_FLAG_STRUCT_MEMBER (VERBOSE << 2) +#define SBITMAPQ_DATA_FLAG_ARRAY_ADDR (VERBOSE << 3) + ulong flags; + int radix; + /* sbitmap_queue info */ + ulong addr; + /* data array info */ + ulong data_addr; + char *data_name; + int data_size; +}; + +#define SB_FLAG_INIT 0x01 + +static uint sb_flags = 0; + +static inline unsigned int __const_hweight8(unsigned long w) +{ + return + (!!((w) & (1ULL << 0))) + + (!!((w) & (1ULL << 1))) + + (!!((w) & (1ULL << 2))) + + (!!((w) & (1ULL << 3))) + + (!!((w) & (1ULL << 4))) + + (!!((w) & (1ULL << 5))) + + (!!((w) & (1ULL << 6))) + + (!!((w) & (1ULL << 7))); +} + +#define __const_hweight16(w) (__const_hweight8(w) + __const_hweight8((w) >> 8)) +#define __const_hweight32(w) (__const_hweight16(w) + __const_hweight16((w) >> 16)) +#define __const_hweight64(w) (__const_hweight32(w) + __const_hweight32((w) >> 32)) + +#define hweight32(w) __const_hweight32(w) +#define hweight64(w) __const_hweight64(w) + +#define BIT(nr) (1UL << (nr)) + +static inline unsigned long min(unsigned long a, unsigned long b) +{ + return (a < b) ? a : b; +} + +static unsigned long __last_word_mask(unsigned long nbits) +{ + return ~0UL >> (-(nbits) & (BITS_PER_LONG - 1)); +} + +static unsigned long bitmap_hweight_long(unsigned long w) +{ + return sizeof(w) == 4 ? hweight32(w) : hweight64(w); +} + +static unsigned long bitmap_weight(unsigned long bitmap, unsigned int bits) +{ + unsigned long w = 0; + + w += bitmap_hweight_long(bitmap); + if (bits % BITS_PER_LONG) + w += bitmap_hweight_long(bitmap & __last_word_mask(bits)); + + return w; +} + +static unsigned int __sbitmap_weight(const struct sbitmap_context *sc, bool set) +{ + const ulong sbitmap_word_size = SIZE(sbitmap_word); + const ulong w_depth_off = OFFSET(sbitmap_word_depth); + const ulong w_word_off = OFFSET(sbitmap_word_word); + const ulong w_cleared_off = OFFSET(sbitmap_word_cleared); + + unsigned int weight = 0; + ulong addr = sc->map_addr; + ulong depth, word, cleared; + char *sbitmap_word_buf; + int i; + + sbitmap_word_buf = GETBUF(sbitmap_word_size); + + for (i = 0; i < sc->map_nr; i++) { + readmem(addr, KVADDR, sbitmap_word_buf, sbitmap_word_size, "sbitmap_word", FAULT_ON_ERROR); + + depth = ULONG(sbitmap_word_buf + w_depth_off); + + if (set) { + word = ULONG(sbitmap_word_buf + w_word_off); + weight += bitmap_weight(word, depth); + } else { + cleared = ULONG(sbitmap_word_buf + w_cleared_off); + weight += bitmap_weight(cleared, depth); + } + + addr += sbitmap_word_size; + } + + FREEBUF(sbitmap_word_buf); + + return weight; +} + +static unsigned int sbitmap_weight(const struct sbitmap_context *sc) +{ + return __sbitmap_weight(sc, true); +} + +static unsigned int sbitmap_cleared(const struct sbitmap_context *sc) +{ + return __sbitmap_weight(sc, false); +} + +static void sbitmap_emit_byte(unsigned int offset, uint8_t byte) +{ + if ((offset &0xf) == 0) { + if (offset != 0) + fputc('\n', fp); + fprintf(fp, "%08x:", offset); + } + if ((offset & 0x1) == 0) + fputc(' ', fp); + fprintf(fp, "%02x", byte); +} + +static void sbitmap_bitmap_show(const struct sbitmap_context *sc) +{ + const ulong sbitmap_word_size = SIZE(sbitmap_word); + const ulong w_depth_off = OFFSET(sbitmap_word_depth); + const ulong w_word_off = OFFSET(sbitmap_word_word); + const ulong w_cleared_off = OFFSET(sbitmap_word_cleared); + + uint8_t byte = 0; + unsigned int byte_bits = 0; + unsigned int offset = 0; + ulong addr = sc->map_addr; + char *sbitmap_word_buf; + int i; + + sbitmap_word_buf = GETBUF(sbitmap_word_size); + + for (i = 0; i < sc->map_nr; i++) { + unsigned long word, cleared, word_bits; + + readmem(addr, KVADDR, sbitmap_word_buf, sbitmap_word_size, "sbitmap_word", FAULT_ON_ERROR); + + word = ULONG(sbitmap_word_buf + w_word_off); + cleared = ULONG(sbitmap_word_buf + w_cleared_off); + word_bits = ULONG(sbitmap_word_buf + w_depth_off); + + word &= ~cleared; + + while (word_bits > 0) { + unsigned int bits = min(8 - byte_bits, word_bits); + + byte |= (word & (BIT(bits) - 1)) << byte_bits; + byte_bits += bits; + if (byte_bits == 8) { + sbitmap_emit_byte(offset, byte); + byte = 0; + byte_bits = 0; + offset++; + } + word >>= bits; + word_bits -= bits; + } + + addr += sbitmap_word_size; + } + if (byte_bits) { + sbitmap_emit_byte(offset, byte); + offset++; + } + if (offset) + fputc('\n', fp); + + FREEBUF(sbitmap_word_buf); +} + +static unsigned long sbitmap_find_next_bit(unsigned long word, + unsigned long size, unsigned long offset) +{ + if (size > BITS_PER_LONG) + error(FATAL, "%s: word size isn't correct\n", __func__); + + for (; offset < size; offset++) + if (word & (1UL << offset)) + return offset; + + return size; +} + +static void __sbitmap_for_each_set(const struct sbitmap_context *sc, + unsigned int start, sbitmap_for_each_fn fn, void *data) +{ + const ulong sbitmap_word_size = SIZE(sbitmap_word); + const ulong w_depth_off = OFFSET(sbitmap_word_depth); + const ulong w_word_off = OFFSET(sbitmap_word_word); + const ulong w_cleared_off = OFFSET(sbitmap_word_cleared); + + unsigned int index; + unsigned int nr; + unsigned int scanned = 0; + char *sbitmap_word_buf; + + sbitmap_word_buf = GETBUF(sbitmap_word_size); + + if (start >= sc->map_nr) + start = 0; + + index = start >> sc->shift; + nr = start & ((1U << sc->shift) - 1U); + + while (scanned < sc->depth) { + unsigned long w_addr = sc->map_addr + (sbitmap_word_size * index); + unsigned long w_depth, w_word, w_cleared; + unsigned long word, depth; + + readmem(w_addr, KVADDR, sbitmap_word_buf, sbitmap_word_size, "sbitmap_word", FAULT_ON_ERROR); + + w_depth = ULONG(sbitmap_word_buf + w_depth_off); + w_word = ULONG(sbitmap_word_buf + w_word_off); + w_cleared = ULONG(sbitmap_word_buf + w_cleared_off); + + depth = min(w_depth - nr, sc->depth - scanned); + + scanned += depth; + word = w_word & ~w_cleared; + if (!word) + goto next; + + /* + * On the first iteration of the outer loop, we need to add the + * bit offset back to the size of the word for find_next_bit(). + * On all other iterations, nr is zero, so this is a noop. + */ + depth += nr; + while (1) { + nr = sbitmap_find_next_bit(word, depth, nr); + if (nr >= depth) + break; + if (!fn((index << sc->shift) + nr, data)) + return; + + nr++; + } +next: + nr = 0; + if (++index >= sc->map_nr) + index = 0; + } + + FREEBUF(sbitmap_word_buf); +} + +void sbitmap_for_each_set(const struct sbitmap_context *sc, + sbitmap_for_each_fn fn, void *data) +{ + __sbitmap_for_each_set(sc, 0, fn, data); +} + +static void sbitmap_queue_show(const struct sbitmap_queue_context *sqc, + const struct sbitmap_context *sc) +{ + int cpus = get_cpus_possible(); + int sbq_wait_state_size, wait_cnt_off, wait_off, list_head_off; + char *sbq_wait_state_buf; + bool first; + int i; + + fprintf(fp, "depth = %u\n", sc->depth); + fprintf(fp, "busy = %u\n", sbitmap_weight(sc) - sbitmap_cleared(sc)); + fprintf(fp, "cleared = %u\n", sbitmap_cleared(sc)); + fprintf(fp, "bits_per_word = %u\n", 1U << sc->shift); + fprintf(fp, "map_nr = %u\n", sc->map_nr); + + fputs("alloc_hint = {", fp); + first = true; + for (i = 0; i < cpus; i++) { + ulong ptr; + int val; + + if (!first) + fprintf(fp, ", "); + first = false; + + ptr = kt->__per_cpu_offset[i] + sqc->alloc_hint; + readmem(ptr, KVADDR, &val, sizeof(val), "alloc_hint", FAULT_ON_ERROR); + + fprintf(fp, "%u", val); + } + fputs("}\n", fp); + + fprintf(fp, "wake_batch = %u\n", sqc->wake_batch); + fprintf(fp, "wake_index = %d\n", sqc->wake_index); + fprintf(fp, "ws_active = %d\n", sqc->ws_active); + + sbq_wait_state_size = SIZE(sbq_wait_state); + wait_cnt_off = OFFSET(sbq_wait_state_wait_cnt); + wait_off = OFFSET(sbq_wait_state_wait); + list_head_off = OFFSET(wait_queue_head_head); + + sbq_wait_state_buf = GETBUF(sbq_wait_state_size); + + fputs("ws = {\n", fp); + for (i = 0; i < SBQ_WAIT_QUEUES; i++) { + ulong ws_addr = sqc->ws_addr + (sbq_wait_state_size * i); + struct kernel_list_head *lh; + ulong wait_cnt; + + readmem(ws_addr, KVADDR, sbq_wait_state_buf, sbq_wait_state_size, "sbq_wait_state", FAULT_ON_ERROR); + + wait_cnt = INT(sbq_wait_state_buf + wait_cnt_off); + lh = (struct kernel_list_head *)(sbq_wait_state_buf + wait_off + list_head_off); + + fprintf(fp, "\t{ .wait_cnt = %lu, .wait = %s },\n", + wait_cnt, (lh->next == lh->prev) ? "inactive" : "active"); + } + fputs("}\n", fp); + + FREEBUF(sbq_wait_state_buf); + + fprintf(fp, "round_robin = %d\n", sqc->round_robin); + fprintf(fp, "min_shallow_depth = %u\n", sqc->min_shallow_depth); +} + +static void sbitmap_queue_context_load(ulong addr, struct sbitmap_queue_context *sqc) +{ + char *sbitmap_queue_buf; + + sqc->sb_addr = addr + OFFSET(sbitmap_queue_sb); + + sbitmap_queue_buf = GETBUF(SIZE(sbitmap_queue)); + readmem(addr, KVADDR, sbitmap_queue_buf, SIZE(sbitmap_queue), "sbitmap_queue", FAULT_ON_ERROR); + + sqc->alloc_hint = ULONG(sbitmap_queue_buf + OFFSET(sbitmap_queue_alloc_hint)); + sqc->wake_batch = UINT(sbitmap_queue_buf + OFFSET(sbitmap_queue_wake_batch)); + sqc->wake_index = INT(sbitmap_queue_buf + OFFSET(sbitmap_queue_wake_index)); + sqc->ws_addr = ULONG(sbitmap_queue_buf + OFFSET(sbitmap_queue_ws)); + sqc->ws_active = INT(sbitmap_queue_buf + OFFSET(sbitmap_queue_ws_active)); + sqc->round_robin = BOOL(sbitmap_queue_buf + OFFSET(sbitmap_queue_round_robin)); + sqc->min_shallow_depth = UINT(sbitmap_queue_buf + OFFSET(sbitmap_queue_min_shallow_depth)); + + FREEBUF(sbitmap_queue_buf); +} + +void sbitmap_context_load(ulong addr, struct sbitmap_context *sc) +{ + char *sbitmap_buf; + + sbitmap_buf = GETBUF(SIZE(sbitmap)); + readmem(addr, KVADDR, sbitmap_buf, SIZE(sbitmap), "sbitmap", FAULT_ON_ERROR); + + sc->depth = UINT(sbitmap_buf + OFFSET(sbitmap_depth)); + sc->shift = UINT(sbitmap_buf + OFFSET(sbitmap_shift)); + sc->map_nr = UINT(sbitmap_buf + OFFSET(sbitmap_map_nr)); + sc->map_addr = ULONG(sbitmap_buf + OFFSET(sbitmap_map)); + + FREEBUF(sbitmap_buf); +} + +static bool for_each_func(unsigned int idx, void *p) +{ + struct sbitmapq_ops *ops = p; + ulong addr = ops->addr + (ops->size * idx); + + return ops->fn(idx, addr, ops->p); +} + +void sbitmapq_for_each_set(ulong addr, struct sbitmapq_ops *ops) +{ + struct sbitmap_queue_context sqc = {0}; + struct sbitmap_context sc = {0}; + + sbitmap_queue_context_load(addr, &sqc); + sbitmap_context_load(sqc.sb_addr, &sc); + + sbitmap_for_each_set(&sc, for_each_func, ops); +} + +static void dump_struct_members(const char *s, ulong addr, unsigned radix) +{ + int i, argc; + char *p1, *p2; + char *structname, *members; + char *arglist[MAXARGS]; + + structname = GETBUF(strlen(s) + 1); + members = GETBUF(strlen(s) + 1); + + strcpy(structname, s); + p1 = strstr(structname, ".") + 1; + + p2 = strstr(s, ".") + 1; + strcpy(members, p2); + replace_string(members, ",", ' '); + argc = parse_line(members, arglist); + + for (i = 0; i < argc; i++) { + *p1 = NULLCHAR; + strcat(structname, arglist[i]); + dump_struct_member(structname, addr, radix); + } + + FREEBUF(structname); + FREEBUF(members); +} + +static bool sbitmap_data_print(unsigned int idx, ulong addr, void *p) +{ + const struct sbitmapq_data *sd = p; + bool verbose = !!(sd->flags & VERBOSE); + bool members = !!(sd->flags & SBITMAPQ_DATA_FLAG_STRUCT_MEMBER); + + if (verbose) { + fprintf(fp, "%d (0x%08lx):\n", idx, addr); + if (members) + dump_struct_members(sd->data_name, addr, sd->radix); + else + dump_struct(sd->data_name, addr, sd->radix); + } else + fprintf(fp, "%d: 0x%08lx\n", idx, addr); + + return true; +} + +static void sbitmap_queue_data_dump(struct sbitmapq_data *sd) +{ + struct sbitmapq_ops ops = { + .addr = sd->data_addr, + .size = sd->data_size, + .fn = sbitmap_data_print, + .p = sd + }; + + sbitmapq_for_each_set(sd->addr, &ops); +} + +static void sbitmap_queue_dump(const struct sbitmapq_data *sd) +{ + struct sbitmap_queue_context sqc ={0}; + struct sbitmap_context sc = {0}; + + sbitmap_queue_context_load(sd->addr, &sqc); + sbitmap_context_load(sqc.sb_addr, &sc); + + sbitmap_queue_show(&sqc, &sc); + fputc('\n', fp); + sbitmap_bitmap_show(&sc); +} + +void sbitmapq_init(void) +{ + if (sb_flags & SB_FLAG_INIT) + return; + + STRUCT_SIZE_INIT(sbitmap_word, "sbitmap_word"); + STRUCT_SIZE_INIT(sbitmap, "sbitmap"); + STRUCT_SIZE_INIT(sbitmap_queue, "sbitmap_queue"); + STRUCT_SIZE_INIT(sbq_wait_state, "sbq_wait_state"); + + MEMBER_OFFSET_INIT(sbitmap_word_depth, "sbitmap_word", "depth"); + MEMBER_OFFSET_INIT(sbitmap_word_word, "sbitmap_word", "word"); + MEMBER_OFFSET_INIT(sbitmap_word_cleared, "sbitmap_word", "cleared"); + + MEMBER_OFFSET_INIT(sbitmap_depth, "sbitmap", "depth"); + MEMBER_OFFSET_INIT(sbitmap_shift, "sbitmap", "shift"); + MEMBER_OFFSET_INIT(sbitmap_map_nr, "sbitmap", "map_nr"); + MEMBER_OFFSET_INIT(sbitmap_map, "sbitmap", "map"); + + MEMBER_OFFSET_INIT(sbitmap_queue_sb, "sbitmap_queue", "sb"); + MEMBER_OFFSET_INIT(sbitmap_queue_alloc_hint, "sbitmap_queue", "alloc_hint"); + MEMBER_OFFSET_INIT(sbitmap_queue_wake_batch, "sbitmap_queue", "wake_batch"); + MEMBER_OFFSET_INIT(sbitmap_queue_wake_index, "sbitmap_queue", "wake_index"); + MEMBER_OFFSET_INIT(sbitmap_queue_ws, "sbitmap_queue", "ws"); + MEMBER_OFFSET_INIT(sbitmap_queue_ws_active, "sbitmap_queue", "ws_active"); + MEMBER_OFFSET_INIT(sbitmap_queue_round_robin, "sbitmap_queue", "round_robin"); + MEMBER_OFFSET_INIT(sbitmap_queue_min_shallow_depth, "sbitmap_queue", "min_shallow_depth"); + + MEMBER_OFFSET_INIT(sbq_wait_state_wait_cnt, "sbq_wait_state", "wait_cnt"); + MEMBER_OFFSET_INIT(sbq_wait_state_wait, "sbq_wait_state", "wait"); + + if (!VALID_SIZE(sbitmap_word) || + !VALID_SIZE(sbitmap) || + !VALID_SIZE(sbitmap_queue) || + !VALID_SIZE(sbq_wait_state) || + INVALID_MEMBER(sbitmap_word_depth) || + INVALID_MEMBER(sbitmap_word_word) || + INVALID_MEMBER(sbitmap_word_cleared) || + INVALID_MEMBER(sbitmap_depth) || + INVALID_MEMBER(sbitmap_shift) || + INVALID_MEMBER(sbitmap_map_nr) || + INVALID_MEMBER(sbitmap_map) || + INVALID_MEMBER(sbitmap_queue_sb) || + INVALID_MEMBER(sbitmap_queue_alloc_hint) || + INVALID_MEMBER(sbitmap_queue_wake_batch) || + INVALID_MEMBER(sbitmap_queue_wake_index) || + INVALID_MEMBER(sbitmap_queue_ws) || + INVALID_MEMBER(sbitmap_queue_ws_active) || + INVALID_MEMBER(sbitmap_queue_round_robin) || + INVALID_MEMBER(sbitmap_queue_min_shallow_depth) || + INVALID_MEMBER(sbq_wait_state_wait_cnt) || + INVALID_MEMBER(sbq_wait_state_wait)) { + command_not_supported(); + } + + sb_flags |= SB_FLAG_INIT; +} + +static char *__get_struct_name(const char *s) +{ + char *name, *p; + + name = GETBUF(strlen(s) + 1); + strcpy(name, s); + + p = strstr(name, "."); + *p = NULLCHAR; + + return name; +} + +void cmd_sbitmapq(void) +{ + struct sbitmapq_data sd = {0}; + int c; + + while ((c = getopt(argcnt, args, "s:a:xdv")) != EOF) { + switch (c) { + case 's': + if (sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME) + error(FATAL, "-s option (%s) already entered\n", sd.data_name); + + sd.data_name = optarg; + sd.flags |= SBITMAPQ_DATA_FLAG_STRUCT_NAME; + + break; + + case 'a': + if (sd.flags & SBITMAPQ_DATA_FLAG_ARRAY_ADDR) + error(FATAL, "-a option (0x%lx) already entered\n", sd.data_addr); + else if (!IS_A_NUMBER(optarg)) + error(FATAL, "invalid -a option: %s\n", optarg); + + sd.data_addr = htol(optarg, FAULT_ON_ERROR, NULL); + if (!IS_KVADDR(sd.data_addr)) + error(FATAL, "invalid kernel virtual address: %s\n", optarg); + sd.flags |= SBITMAPQ_DATA_FLAG_ARRAY_ADDR; + + break; + + case 'v': + sd.flags |= VERBOSE; + break; + + case 'x': + if (sd.radix == 10) + error(FATAL, "-d and -x are mutually exclusive\n"); + sd.radix = 16; + break; + + case 'd': + if (sd.radix == 16) + error(FATAL, "-d and -x are mutually exclusive\n"); + sd.radix = 10; + break; + + default: + argerrs++; + break; + } + } + + if (argerrs) + cmd_usage(pc->curcmd, SYNOPSIS); + + if (!args[optind]) { + error(INFO, "command argument is required\n"); + cmd_usage(pc->curcmd, SYNOPSIS); + } else if (args[optind] && args[optind + 1]) { + error(INFO, "too many arguments\n"); + cmd_usage(pc->curcmd, SYNOPSIS); + } else if (!IS_A_NUMBER(args[optind])) { + error(FATAL, "invalid command argument: %s\n", args[optind]); + } + + sd.addr = htol(args[optind], FAULT_ON_ERROR, NULL); + if (!IS_KVADDR(sd.addr)) + error(FATAL, "invalid kernel virtual address: %s\n", args[optind]); + + if ((sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME) && + !(sd.flags & SBITMAPQ_DATA_FLAG_ARRAY_ADDR)) { + error(INFO, "-s option requires -a option\n"); + cmd_usage(pc->curcmd, SYNOPSIS); + } else if ((sd.flags & SBITMAPQ_DATA_FLAG_ARRAY_ADDR) && + !(sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME)) { + error(INFO, "-a option is used with -s option only\n"); + cmd_usage(pc->curcmd, SYNOPSIS); + } + + if (sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME) { + if (count_chars(sd.data_name, '.') > 0) + sd.flags |= SBITMAPQ_DATA_FLAG_STRUCT_MEMBER; + + if (sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_MEMBER) { + char *data_name = __get_struct_name(sd.data_name); + sd.data_size = STRUCT_SIZE(data_name); + FREEBUF(data_name); + } else + sd.data_size = STRUCT_SIZE(sd.data_name); + + if (sd.data_size <= 0) + error(FATAL, "invalid data structure reference: %s\n", sd.data_name); + } + + sbitmapq_init(); + + if (sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME) + sbitmap_queue_data_dump(&sd); + else + sbitmap_queue_dump(&sd); +} diff --git a/symbols.c b/symbols.c index 73baa95..12570a8 100644 --- a/symbols.c +++ b/symbols.c @@ -10690,6 +10690,41 @@ dump_offset_table(char *spec, ulong makestruct) fprintf(fp, " uts_namespace_name: %ld\n", OFFSET(uts_namespace_name)); + fprintf(fp, " sbitmap_word_depth: %ld\n", + OFFSET(sbitmap_word_depth)); + fprintf(fp, " sbitmap_word_word: %ld\n", + OFFSET(sbitmap_word_word)); + fprintf(fp, " sbitmap_word_cleared: %ld\n", + OFFSET(sbitmap_word_cleared)); + fprintf(fp, " sbitmap_depth: %ld\n", + OFFSET(sbitmap_depth)); + fprintf(fp, " sbitmap_shift: %ld\n", + OFFSET(sbitmap_shift)); + fprintf(fp, " sbitmap_map_nr: %ld\n", + OFFSET(sbitmap_map_nr)); + fprintf(fp, " sbitmap_map: %ld\n", + OFFSET(sbitmap_map)); + fprintf(fp, " sbitmap_queue_sb: %ld\n", + OFFSET(sbitmap_queue_sb)); + fprintf(fp, " sbitmap_queue_alloc_hint: %ld\n", + OFFSET(sbitmap_queue_alloc_hint)); + fprintf(fp, " sbitmap_queue_wake_batch: %ld\n", + OFFSET(sbitmap_queue_wake_batch)); + fprintf(fp, " sbitmap_queue_wake_index: %ld\n", + OFFSET(sbitmap_queue_wake_index)); + fprintf(fp, " sbitmap_queue_ws: %ld\n", + OFFSET(sbitmap_queue_ws)); + fprintf(fp, " sbitmap_queue_ws_active: %ld\n", + OFFSET(sbitmap_queue_ws_active)); + fprintf(fp, " sbitmap_queue_round_robin: %ld\n", + OFFSET(sbitmap_queue_round_robin)); + fprintf(fp, "sbitmap_queue_min_shallow_depth: %ld\n", + OFFSET(sbitmap_queue_min_shallow_depth)); + fprintf(fp, " sbq_wait_state_wait_cnt: %ld\n", + OFFSET(sbq_wait_state_wait_cnt)); + fprintf(fp, " sbq_wait_state_wait: %ld\n", + OFFSET(sbq_wait_state_wait)); + fprintf(fp, "\n size_table:\n"); fprintf(fp, " page: %ld\n", SIZE(page)); fprintf(fp, " page_flags: %ld\n", SIZE(page_flags)); @@ -10955,6 +10990,10 @@ dump_offset_table(char *spec, ulong makestruct) fprintf(fp, " printk_ringbuffer: %ld\n", SIZE(printk_ringbuffer)); fprintf(fp, " prb_desc: %ld\n", SIZE(prb_desc)); + fprintf(fp, " sbitmap_word: %ld\n", SIZE(sbitmap_word)); + fprintf(fp, " sbitmap: %ld\n", SIZE(sbitmap)); + fprintf(fp, " sbitmap_queue: %ld\n", SIZE(sbitmap_queue)); + fprintf(fp, " sbq_wait_state: %ld\n", SIZE(sbq_wait_state)); fprintf(fp, "\n array_table:\n"); /* -- 2.25.1 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/crash-utility