Hi, Sergey

Date: Mon, 4 Oct 2021 20:15:36 +0300
From: Sergey Samoylenko <s.samoylenko@yadro.com>
To: <crash-utility@redhat.com>
Cc: Sergey Samoylenko <s.samoylenko@yadro.com>, linux@yadro.com
Subject: [Crash-utility] [PATCH] New sbitmap command
Message-ID: <20211004171536.30971-1-s.samoylenko@yadro.com>
Content-Type: text/plain

Patch adds new 'sbitmap' 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.

Signed-off-by: Sergey Samoylenko <s.samoylenko@yadro.com>
---
 Makefile      |   7 +-
 defs.h        |   2 +
 global_data.c |   1 +
 help.c        |  89 ++++++++
 sbitmap.c     | 591 ++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 688 insertions(+), 2 deletions(-)
 create mode 100644 sbitmap.c

diff --git a/Makefile b/Makefile
index ece1306..c116177 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

@@ -346,6 +346,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 eb1c71b..1fa5c21 100644
--- a/defs.h
+++ b/defs.h
@@ -4956,6 +4956,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_sbitmap(void);      /* sbitmap.c */
 void cmd_bpf(void);          /* bfp.c */
 void cmd_set(void);          /* tools.c */
 void cmd_eval(void);         /* tools.c */
@@ -5543,6 +5544,7 @@ void display_help_screen(char *);
 extern char *help_pointer[];
 extern char *help_alias[];
 extern char *help_ascii[];
+extern char *help_sbitmap[];
 extern char *help_bpf[];
 extern char *help_bt[];
 extern char *help_btop[];
diff --git a/global_data.c b/global_data.c
index a316d1c..55524e3 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},
+       {"sbitmap", cmd_sbitmap, help_sbitmap, 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 6c262a3..b946745 100644
--- a/help.c
+++ b/help.c
@@ -967,6 +967,95 @@ char *help_ascii[] = {
 NULL
 };

+char *help_sbitmap[] = {
+"sbitmap",
+"sbitmap_queue struct contents",
+"[-s struct[.member[,member]] -p address [-v]] address",
+"  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.",
+"",
+"  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",
+"               \"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 of a formatted display of the",
+"               contents of a structure in an associated array. -v option",
+"               requires of -s.",
+"",
+"EXAMPLES",
+"",
+"  Display the common sbitmap information:",
+"",
+"    %s> sbitmap ffffffffc06b9420",
+"    depth = 256",
+"    busy = 15",
+"    cleared = 1",
+"    bits_per_word = 64",
+"    map_nr = 4",
+"    alloc_hint = {193, 78}",
+"    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 ffbf 0000 0000 0000",
+"    00000010: 0000 0000 0000 0000 0000 0000 0000 0000",
+"",
+"  Display the structure address is associated with sbitmap_queue:",
+"",

Other changes look good to me.  But  I didn't see the relationship between test_data and sbitmap_queue according to the example.
Could  you please describe more details about what's the structure associated with sbitmap_queue? Thanks.
 
+"    %s> sbitmap -s test_data -p ffff973422dac000 ffffffffc06b9420",
+"    64: 0xffff973422dac200",
+"    65: 0xffff973422dac208",
+"    66: 0xffff973422dac210",
+"    ...",
+"",
+"  Display a formatted content of a structures:",
+"",
+"    %s> sbitmap -s test_data -p ffff973422dac000 -v ffffffffc06b9420",
+"    64 (0xffff973422dac200):",
+"    struct test_data {",
+"      tag = 64,",
+"      cpu = 1",
+"    }",
+"    65 (0xffff973422dac208):",
+"    struct test_data {",
+"      tag = 65,",
+"      cpu = 1",
+"    }",
+"    66 (0xffff973422dac210):",
+"    struct test_data {",
+"      tag = 66,",
+"      cpu = 1",
+"    }",
+"    ...",
+NULL
+};
+
 char *help_quit[] = {
 "quit",
 "exit this session",
diff --git a/sbitmap.c b/sbitmap.c
new file mode 100644
index 0000000..71522f8
--- /dev/null
+++ b/sbitmap.c
@@ -0,0 +1,591 @@
+/* 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.
+ *
+ * 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"
+#include <stdbool.h>
+
+#define SBQ_WAIT_QUEUES 8
+
+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))
+
+/* sbitmap and sbitmap_word structs context */
+struct sbitmap_context {
+       unsigned sb_depth;
+       unsigned sb_shift;
+       unsigned sb_map_nr;
+       ulong sb_map_addr;
+       unsigned sb_map_size;
+
+       int w_depth_off;
+       int w_word_off;
+       int w_cleared_off;
+};
+
+/* 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 sbitmap_data {
+#define SD_FLAG_STRUCT_NAME      (VERBOSE << 1)
+#define SD_FLAG_STRUCT_ADDR      (VERBOSE << 2)
+#define SD_FLAG_STRUCT_MEMBER    (VERBOSE << 3)
+       ulong flags;
+       int radix;
+       /* sbitmap_queue info */
+       ulong addr;
+       /* data array info */
+       char *data_name;
+       ulong data_addr;
+
+       struct sbitmap_queue_context sqc;
+       struct sbitmap_context sc;
+};
+
+static inline unsigned long min(unsigned long a, unsigned long b)
+{
+       return (a < b) ? a : b;
+}
+
+static void __readmem(ulong addr, void *buffer, size_t size)
+{
+       ulong flag = FAULT_ON_ERROR;
+
+       if (!readmem(addr, KVADDR, buffer, size, "__readmem", flag))
+               error(FATAL, "failed read memory: 0x%x\n", addr);
+}
+
+static ulong __read_ulong(ulong addr)
+{
+       ulong value;
+
+       __readmem(addr, &value, sizeof(value));
+       return value;
+}
+
+static long __struct_size(char *name)
+{
+       long size;
+
+       size = STRUCT_SIZE(name);
+       if (size < 0)
+               error(FATAL, "Invalid struct size: %s\n", name);
+
+       return size;
+}
+
+static long __member_offset(char *name, char *member)
+{
+       long offset;
+
+       offset = MEMBER_OFFSET(name, member);
+       if (offset == INVALID_OFFSET)
+               offset = ANON_MEMBER_OFFSET(name, member);
+       if (offset == INVALID_OFFSET)
+               error(FATAL, "Can't get offset of '%s.%s'\n", name, member);
+
+       return offset;
+}
+
+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)
+{
+       unsigned int weight = 0;
+       ulong addr = sc->sb_map_addr;
+       unsigned long depth, word, cleared;
+       int i;
+
+       for (i = 0; i < sc->sb_map_nr; i++) {
+               depth = __read_ulong(addr + sc->w_depth_off);
+
+               if (set) {
+                       word = __read_ulong(addr + sc->w_word_off);
+                       weight += bitmap_weight(word, depth);
+               } else {
+                       cleared = __read_ulong(addr + sc->w_cleared_off);
+                       weight += bitmap_weight(cleared, depth);
+               }
+
+               addr += sc->sb_map_size;
+       }
+
+       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_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;
+       bool first;
+       int i;
+
+       fprintf(fp, "depth = %u\n", sc->sb_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->sb_shift);
+       fprintf(fp, "map_nr = %u\n", sc->sb_map_nr);
+
+       fputs("alloc_hint = {", fp);
+       first = true;
+       for (i = 0; i < cpus; i++) {
+               if (!first)
+                       fprintf(fp, ", ");
+               first = false;
+
+               ulong ptr = kt->__per_cpu_offset[i] + sqc->alloc_hint;
+               fprintf(fp, "%lu", __read_ulong(ptr));
+       }
+       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 = __struct_size("sbq_wait_state");
+       wait_cnt_off = __member_offset("sbq_wait_state", "wait_cnt");
+       wait_off = __member_offset("sbq_wait_state", "wait");
+       list_head_off = __member_offset("wait_queue_head", "head");
+
+       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;
+               ulong wait_cnt;
+
+               wait_cnt_addr = ws_addr + wait_cnt_off;
+               __readmem(wait_cnt_addr, &wait_cnt, sizeof(wait_cnt));
+
+               list_head_addr = ws_addr + wait_off + list_head_off;
+               __readmem(list_head_addr, &lh, sizeof(lh));
+
+               fprintf(fp, "\t{ .wait_cnt = %lu, .wait = %s },\n",
+                       wait_cnt, (lh.next == lh.prev) ? "inactive" : "active");
+       }
+       fputs("}\n", fp);
+
+       fprintf(fp, "round_robin = %d\n", sqc->round_robin);
+       fprintf(fp, "min_shallow_depth = %u\n", sqc->min_shallow_depth);
+}
+
+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)
+{
+       uint8_t byte = 0;
+       unsigned int byte_bits = 0;
+       unsigned int offset = 0;
+       int i;
+
+       for (i = 0; i < sc->sb_map_nr; i++) {
+               ulong addr = sc->sb_map_addr + (sc->sb_map_size * i);
+               unsigned long word = __read_ulong(addr + sc->w_word_off);
+               unsigned long cleared = __read_ulong(addr + sc->w_cleared_off);
+               unsigned long word_bits = __read_ulong(addr + sc->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;
+               }
+
+       }
+       if (byte_bits) {
+               sbitmap_emit_byte(offset, byte);
+               offset++;
+       }
+       if (offset)
+               fputc('\n', fp);
+}
+
+static void sbitmap_queue_dump(const struct sbitmap_data *sd)
+{
+       sbitmap_queue_show(&sd->sqc, &sd->sc);
+       fputc('\n', fp);
+       sbitmap_bitmap_show(&sd->sc);
+}
+
+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;
+}
+
+typedef bool (*sb_for_each_fn)(unsigned int, void *);
+
+static void __sbitmap_for_each_set(const struct sbitmap_context *sc,
+               unsigned int start, sb_for_each_fn fn, void *data)
+{
+       unsigned int index;
+       unsigned int nr;
+       unsigned int scanned = 0;
+
+       if (start >= sc->sb_map_nr)
+               start = 0;
+
+       index = start >> sc->sb_shift;
+       nr = start & ((1U << sc->sb_shift) - 1U);
+
+       while (scanned < sc->sb_depth) {
+               unsigned long w_addr = sc->sb_map_addr + (sc->sb_map_size * index);
+
+               unsigned long w_depth = __read_ulong(w_addr + sc->w_depth_off);
+               unsigned long w_word = __read_ulong(w_addr + sc->w_word_off);
+               unsigned long w_cleared = __read_ulong(w_addr + sc->w_cleared_off);
+
+               unsigned long word;
+               unsigned int depth = min(w_depth - nr, sc->sb_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->sb_shift) + nr, data))
+                               return;
+
+                       nr++;
+               }
+next:
+               nr = 0;
+               if (++index >= sc->sb_map_nr)
+                       index = 0;
+       }
+}
+
+static void sbitmap_for_each_set(const struct sbitmap_context *sc,
+               sb_for_each_fn fn, void *data)
+{
+       __sbitmap_for_each_set(sc, 0, fn, data);
+}
+
+static void sbitmap_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);
+}
+
+struct data_info {
+       ulong addr;
+       int size;
+       char *name;
+       unsigned radix;
+       bool verbose;
+       bool members;
+};
+
+static bool sbitmap_data_print(unsigned int idx, void *data)
+{
+       const struct data_info *d = data;
+       ulong addr = d->addr + (d->size * idx);
+
+       if (d->verbose) {
+               fprintf(fp, "%d (0x%08lx):\n", idx, addr);
+               if (d->members)
+                       sbitmap_dump_struct_members(d->name, addr, d->radix);
+               else
+                       dump_struct(d->name, addr, d->radix);
+       } else
+               fprintf(fp, "%d: 0x%08lx\n", idx, addr);
+
+       return true;
+}
+
+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;
+}
+
+static void sbitmap_data_dump(const struct sbitmap_data *sd)
+{
+       struct data_info d = {0};
+
+       d.addr = sd->data_addr;
+       d.name = sd->data_name;
+       d.radix = sd->radix;
+       d.verbose = !!(sd->flags & VERBOSE);
+       d.members = !!(sd->flags & SD_FLAG_STRUCT_MEMBER);
+
+       if (d.members) {
+               char *name = __get_struct_name(d.name);
+               d.size = __struct_size(name);
+               FREEBUF(name);
+       } else
+               d.size = __struct_size(d.name);
+
+       sbitmap_for_each_set(&sd->sc, sbitmap_data_print, &d);
+}
+
+static void load_sbitmap_queue_context(ulong addr, struct sbitmap_queue_context *sqc)
+{
+       char *sb_q = "sbitmap_queue";
+
+       sqc->sb_addr = addr + __member_offset(sb_q, "sb");
+       __readmem(addr + __member_offset(sb_q, "alloc_hint"), &sqc->alloc_hint, sizeof(sqc->alloc_hint));
+       __readmem(addr + __member_offset(sb_q, "wake_batch"), &sqc->wake_batch, sizeof(sqc->wake_batch));
+       __readmem(addr + __member_offset(sb_q, "wake_index"), &sqc->wake_index, sizeof(sqc->wake_index));
+       __readmem(addr + __member_offset(sb_q, "ws"), &sqc->ws_addr, sizeof(sqc->ws_addr));
+       __readmem(addr + __member_offset(sb_q, "ws_active"), &sqc->ws_active, sizeof(sqc->ws_active));
+       __readmem(addr + __member_offset(sb_q, "round_robin"), &sqc->round_robin, sizeof(sqc->round_robin));
+       __readmem(addr + __member_offset(sb_q, "min_shallow_depth"), &sqc->min_shallow_depth, sizeof(sqc->min_shallow_depth));
+}
+
+static void load_sbitmap_context(ulong addr, struct sbitmap_context *sc)
+{
+       char *sb = "sbitmap";
+       char *map = "sbitmap_word";
+
+       __readmem(addr + __member_offset(sb, "depth"), &sc->sb_depth, sizeof(sc->sb_depth));
+       __readmem(addr + __member_offset(sb, "shift"), &sc->sb_shift, sizeof(sc->sb_shift));
+       __readmem(addr + __member_offset(sb, "map_nr"), &sc->sb_map_nr, sizeof(sc->sb_map_nr));
+       __readmem(addr + __member_offset(sb, "map"), &sc->sb_map_addr, sizeof(sc->sb_map_addr));
+       sc->sb_map_size = __struct_size(map);
+       sc->w_depth_off = __member_offset(map, "depth");
+       sc->w_word_off = __member_offset(map, "word");
+       sc->w_cleared_off = __member_offset(map, "cleared");
+}
+
+void cmd_sbitmap(void)
+{
+       struct sbitmap_data sd = {0};
+       int c;
+
+       while ((c = getopt(argcnt, args, "s:p:xdv")) != EOF) {
+               switch (c) {
+               case 's':
+                       if (sd.flags & SD_FLAG_STRUCT_NAME)
+                               error(FATAL, "-s option (%s) already entered\n", sd.data_name);
+
+                       sd.data_name = optarg;
+                       sd.flags |= SD_FLAG_STRUCT_NAME;
+
+                       break;
+
+               case 'p':
+                       if (sd.flags & SD_FLAG_STRUCT_ADDR)
+                               error(FATAL, "-m option (0x%lx) already entered\n", sd.data_addr);
                                                            ^^
The "-m" should be the "-p" option,right?

Thanks.
Lianbo

+                       else if (!IS_A_NUMBER(optarg))
+                               error(FATAL, "invalid -m option: %s\n", optarg);

Ditto. 
+
+                       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 |= SD_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 & SD_FLAG_STRUCT_NAME) && !(sd.flags & SD_FLAG_STRUCT_ADDR)) {
+               error(INFO, "-s option requires -m option");

Ditto.
+               cmd_usage(pc->curcmd, SYNOPSIS);
+       } else if ((sd.flags & SD_FLAG_STRUCT_ADDR) && !(sd.flags & SD_FLAG_STRUCT_NAME)) {
+               error(FATAL, "-m option is used with -s option only\n");

Ditto. 
+               cmd_usage(pc->curcmd, SYNOPSIS);
+       }
+
+       if (sd.flags & SD_FLAG_STRUCT_NAME) {
+               bool error_flag = false;
+
+               if (count_chars(sd.data_name, '.') > 0)
+                       sd.flags |= SD_FLAG_STRUCT_MEMBER;
+
+               if (sd.flags & SD_FLAG_STRUCT_MEMBER) {
+                       char *data_name = __get_struct_name(sd.data_name);
+                       if (!STRUCT_EXISTS(data_name))
+                               error_flag = true;
+                       FREEBUF(data_name);
+               } else {
+                       if (!STRUCT_EXISTS(sd.data_name))
+                               error_flag = true;
+               }
+               if (error_flag)
+                       error(FATAL, "invalid data structure reference: %s\n", sd.data_name);
+       }
+
+       load_sbitmap_queue_context(sd.addr, &sd.sqc);
+       load_sbitmap_context(sd.sqc.sb_addr, &sd.sc);
+
+       if (sd.flags & SD_FLAG_STRUCT_NAME)
+               sbitmap_data_dump(&sd);
+       else
+               sbitmap_queue_dump(&sd);
+}
-- 
2.25.1