Hi Sergey, -----Original Message----- > 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) Thank you for the update, the patch looks nice and clean! I have two slight questions inline with "Question:". As for the others, I've changed the code at my end and been testing, so I can post it when the questions are solved. > > Signed-off-by: Sergey Samoylenko <s.samoylenko@xxxxxxxxx> > --- > Makefile | 7 +- > defs.h | 59 +++++ > global_data.c | 1 + > help.c | 108 ++++++++ > sbitmap.c | 664 ++++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 837 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; > }; The corresponding entries are needed in "help -o" output. --- a/symbols.c +++ b/symbols.c @@ -10690,6 +10690,24 @@ 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 +10973,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"); /* > > 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..d151f2e 100644 > --- a/help.c > +++ b/help.c > @@ -962,6 +962,114 @@ char *help_ascii[] = { > NULL > }; > > +char *help_sbitmapq[] = { > +"sbitmapq", > +"sbitmap_queue struct contents", > +"[-s struct[.member[,member]] -p address [-v]] 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", > +" sssociated with sbitmap_queue structure. Use the", s/sssociated/associated/ > +" \"struct.member\" format in order to display a particular", > +" member of the structure. -s option requires -p option", > +"", > +" -p 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.", I'd like to change these to the crash style like this: " -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 -p option", " -p 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 whit iSCSI", s/whit/with/ > +" 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 -p 0xc0000000671c0000 c0000000e118c808", > +" 76: 0xc0000000671d5600", > +" 77: 0xc0000000671d5a80", > +"", > + > +" Dump of formatted content of structures:", > +"", > +" %s> sbitmapq -s iscsi_cmd -p 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..5343a88 > --- /dev/null > +++ b/sbitmap.c > @@ -0,0 +1,664 @@ > +/* sbitmap.c - core analysis suite > + * > + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. > + * Copyright (C) 2002-2020 David Anderson > + * Copyright (C) 2002-2020 Red Hat, Inc. All rights reserved. Question: Would you like to change these copyright notices to yours? If you have a proper one, please let me know. e.g. in crash_target.c: * Copyright (c) 2021 VMware, Inc. > + * > + * 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_ADDR (VERBOSE << 2) > +#define SBITMAPQ_DATA_FLAG_STRUCT_MEMBER (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; Question: Is "(start >= sc->depth)" correct here? Currently start is always zero, though. > + > + 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_addr, list_head_addr; will remove these unused variables: $ make clean ; make warn ... sbitmap.c: In function ‘sbitmap_queue_show’: sbitmap.c:342:24: warning: unused variable ‘list_head_addr’ [-Wunused-variable] ulong wait_cnt_addr, list_head_addr; ^~~~~~~~~~~~~~ sbitmap.c:342:9: warning: unused variable ‘wait_cnt_addr’ [-Wunused-variable] ulong wait_cnt_addr, list_head_addr; ^~~~~~~~~~~~~ > + 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:p: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 'p': > + if (sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_ADDR) > + error(FATAL, "-p option (0x%lx) already entered\n", sd.data_addr); > + else if (!IS_A_NUMBER(optarg)) > + error(FATAL, "invalid -p 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_STRUCT_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_STRUCT_ADDR)) { > + error(INFO, "-s option requires -p option"); > + cmd_usage(pc->curcmd, SYNOPSIS); > + } else if ((sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_ADDR) && > + !(sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME)) { > + error(FATAL, "-p option is used with -s option only\n"); > + cmd_usage(pc->curcmd, SYNOPSIS); > + } > + > + if (sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME) { > + bool error_flag = false; > + > + 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); > + if (sd.data_size <= 0) > + error_flag = true; > + > + FREEBUF(data_name); > + } else { > + sd.data_size = STRUCT_SIZE(sd.data_name); > + if (sd.data_size <= 0) > + error_flag = true; > + } > + if (error_flag) > + error(FATAL, "invalid data structure reference: %s\n", sd.data_name); > + } The error_flag can be removed and this block can be shortened, will change like this: 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); } Thanks, Kazu > + > + sbitmapq_init(); > + > + if (sd.flags & SBITMAPQ_DATA_FLAG_STRUCT_NAME) > + sbitmap_queue_data_dump(&sd); > + else > + sbitmap_queue_dump(&sd); > +} > -- > 2.25.1 > > > -- > Crash-utility mailing list > Crash-utility@xxxxxxxxxx > https://listman.redhat.com/mailman/listinfo/crash-utility -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/crash-utility