[PATCH 11/11] Add percpu handling to struct/union/* commands

Extend the argument syntax of struct/union/* commands to recognize
percpu specifiers. Note that percpu addresses are not valid kernel
virtual addresses, so they must be parsed separately.

Signed-off-by: Petr Tesarik <ptesarik@suse.cz>

---
 help.c    |   18 ++++++++++++++----
 symbols.c |   47 ++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 60 insertions(+), 5 deletions(-)

--- a/help.c
+++ b/help.c
@@ -4104,8 +4104,8 @@ NULL
 char *help_struct[] = {
 "struct",
 "structure contents",
-"struct_name[.member[,member]][-o][-l offset][-rfuxdp][address | symbol]\n"
-"         [count | -c count]",
+"struct_name[.member[,member]][-o][-l offset][-rfuxdp]\n"
+"         [address | symbol][:cpuspec] [count | -c count]",
 "  This command displays either a structure definition, or a formatted display",
 "  of the contents of a structure at a specified address.  When no address is",
 "  specified, the structure definition is shown along with the structure size.",
@@ -4139,6 +4139,11 @@ char *help_struct[] = {
 "                 to an embedded list_head structure contained within the",
 "                 target data structure, then the \"-l\" option must be used.",
 "         symbol  symbolic reference to the address of a structure.",
+"        cpuspec  CPU specification for per-cpu variables:",
+"                  :             CPU of the currently selected task.",
+"                  :a[ll]        all CPUs.",
+"                  :#[-#][,...]  CPU list(s), e.g. \"1,3,5\", \"1-3\",",
+"                                or \"1,3,5-7,10\".",
 "          count  count of structures to dump from an array of structures;",
 "                 if used, this must be the last argument entered.",
 "       -c count  \"-c\" is only required if \"count\" is not the last argument",
@@ -4394,8 +4399,8 @@ NULL
 char *help_union[] = {
 "union",
 "union contents",
-"union_name[.member[,member]] [-o][-l offset][-rfuxdp] [address | symbol]\n"
-"                                     [count | -c count]",
+"union_name[.member[,member]] [-o][-l offset][-rfuxdp]\n"
+"         [address | symbol][:cpuspec] [count | -c count]",
 "  This command displays either a union definition, or a formatted display",
 "  of the contents of a union at a specified address.  When no address is",
 "  specified, the union definition is shown along with the union size.",
@@ -4430,6 +4435,11 @@ char *help_union[] = {
 "                 to an embedded list_head structure contained within the",
 "                 target union structure, then the \"-l\" option must be used.",
 "         symbol  symbolic reference to the address of a union.",
+"        cpuspec  CPU specification for per-cpu variables:",
+"                  :             CPU of the currently selected task.",
+"                  :a[ll]        all CPUs.",
+"                  :#[-#][,...]  CPU list(s), e.g. \"1,3,5\", \"1-3\",",
+"                                or \"1,3,5-7,10\".",
 "          count  count of unions to dump from an array of unions; if used,",
 "                 this must be the last argument entered.",
 "       -c count  \"-c\" is only required if \"count\" is not the last argument",
--- a/symbols.c
+++ b/symbols.c
@@ -5712,6 +5712,8 @@ cmd_datatype_common(ulong flags)
 {
 	int c;
 	ulong addr, aflag;
+	char *cpuspec;
+	ulong *cpus;
 	struct syment *sp;
 	ulong list_head_offset;
 	int count;
@@ -5730,6 +5732,8 @@ cmd_datatype_common(ulong flags)
         argc_members = 0;
 	radix = restore_radix = 0;
 	separator = members = NULL;
+	cpuspec = NULL;
+	cpus = NULL;
 
         while ((c = getopt(argcnt, args, "pxdhfuc:rvol:")) != EOF) {
                 switch (c)
@@ -5818,11 +5822,22 @@ cmd_datatype_common(ulong flags)
 		if (aflag && (count != 0xdeadbeef))
 			error(FATAL, "too many arguments!\n");
 
+		if (!aflag) {
+			cpuspec = strchr(args[optind], ':');
+			if (cpuspec)
+				*cpuspec++ = NULLCHAR;
+		}
+
 		if (clean_arg() && IS_A_NUMBER(args[optind])) { 
 			if (aflag) 
 				count = stol(args[optind], 
 					FAULT_ON_ERROR, NULL);
-			else {
+			else if (cpuspec) {
+				if (pc->curcmd_flags & MEMTYPE_FILEADDR)
+					error(FATAL, "percpu cannot be used with dumpfile offsets\n");
+				addr = htol(args[optind], FAULT_ON_ERROR, NULL);
+				aflag++;
+			} else {
 				if (pc->curcmd_flags & MEMTYPE_FILEADDR)
 					pc->curcmd_private = stoll(args[optind], 
 						FAULT_ON_ERROR, NULL);
@@ -5837,6 +5852,12 @@ cmd_datatype_common(ulong flags)
 				aflag++;
 			}
 		} else if ((sp = symbol_search(args[optind]))) {
+			if (cpuspec && !is_percpu_symbol(sp)) {
+				error(WARNING,
+				      "%s is not percpu; cpuspec ignored.\n",
+				      sp->name);
+				cpuspec = NULL;
+			}
 	                addr = sp->value;
 			aflag++;
 	        } else {
@@ -5848,6 +5869,15 @@ cmd_datatype_common(ulong flags)
 		}
 	}
 
+	if (cpuspec) {
+		cpus = get_cpumask_buf();
+		if (STREQ(cpuspec, "")) {
+			SET_BIT(cpus, CURRENT_CONTEXT()->processor);
+		} else {
+			make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL);
+		}
+	}
+
 	optind = optind_save;
 
 	if (count == 0xdeadbeef)
@@ -5914,6 +5944,18 @@ cmd_datatype_common(ulong flags)
 			error(FATAL, "invalid data structure reference: %s.%s\n",
 			      dm->name, memberlist[0]);
 		do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF));
+	} else if (cpus) {
+		for (c = 0; c < kt->cpus; c++) {
+			ulong cpuaddr;
+
+			if (!NUM_IN_BITMAP(cpus, c))
+				continue;
+
+			cpuaddr = addr + kt->__per_cpu_offset[c];
+			fprintf(fp, "  [%d]: %lx\n", c, cpuaddr);
+			do_datatype_addr(dm, cpuaddr , count,
+					 flags, memberlist, argc_members);
+		}
 	} else
 		do_datatype_addr(dm, addr, count, flags,
 				 memberlist, argc_members);
@@ -5925,6 +5967,9 @@ freebuf:
                 FREEBUF(structname);
                 FREEBUF(members);
 	}
+
+	if (cpus)
+		FREEBUF(cpus);
 }
 
 static void
