André Detsch wrote:
Hi,
Here is an updated version of the spu crash extension. It is meant to
replace the version at:
http://people.redhat.com/anderson/extensions/spu.c
Sending a diff as well, for reference.
The previous version was trying to access the spu context field even
when the field is == NULL (which is true in idle physicals spus). That
bug leads to the following error message:
crash> spus
NODE 0:
ID SPUADDR SPUSTATUS CTXADDR CTXSTATE PID
spus: invalid kernel virtual address: 858 type: "print_spu_header get
ctxstate"
Besides fixing this issue, the patch also cleans up some trailing white
spaces.
PS: Lucio, the original author, is still working at IBM, but on another
project. So, I'm taking care of the extension for him.
OK, thanks. I'll update the upstream version on my people site.
However, more importantly is how this impacts the proposed introduction
of the RHEL5.3 crash-spu-commands package. I will follow up with a private
email concerning that issue.
Dave
Cheers,
------------------------------------------------------------------------
--- spu.c.orig 2007-09-21 15:42:20.000000000 -0300
+++ spu.c 2008-07-22 19:30:58.000000000 -0300
@@ -208,13 +208,13 @@
/*
- * Returns a pointer to the requested SPU field
+ * Returns a pointer to the requested SPU field
*/
ulong get_spu_addr(ulong spu_info)
{
ulong spu_addr;
- readmem(spu_info + CBE_OFFSET(crash_spu_info, spu), KVADDR, &spu_addr,
+ readmem(spu_info + CBE_OFFSET(crash_spu_info, spu), KVADDR, &spu_addr,
sizeof(spu_addr), "get_spu_addr", FAULT_ON_ERROR);
return spu_addr;
@@ -252,8 +252,8 @@
cast(debug_data + offset)); \
} while(0)
-/*
- * Print the spu and spu_context structs fields. Some SPU memory-mapped IO
+/*
+ * Print the spu and spu_context structs fields. Some SPU memory-mapped IO
* registers are taken directly from crash_spu_info.
*/
void print_ctx_info(char *ctx_data, char *spu_data, int info)
@@ -314,6 +314,9 @@
long size, offset;
ulong spu_addr, addr;
+ if (!ctx_addr)
+ return;
+
spu_data = NULL;
info = 0;
@@ -321,7 +324,7 @@
ctx_data = GETBUF(size);
if (!ctx_data)
error(FATAL, "Couldn't allocate memory for ctx.\n");
- readmem(ctx_addr, KVADDR, ctx_data, size, "show_ctx_info ctx",
+ readmem(ctx_addr, KVADDR, ctx_data, size, "show_ctx_info ctx",
FAULT_ON_ERROR);
spu_addr = *(ulong *)(ctx_data + CBE_OFFSET(spu_context, spu));
@@ -359,17 +362,18 @@
int i, j, cnt;
long prio_size, prio_runq_off, ctx_rq_off, jump, offset, ctxs_size;
char *u_spu_prio;
- ulong spu_prio_addr, k_spu_prio, kvaddr, uvaddr, addr, ctx;
+ ulong spu_prio_addr, k_spu_prio, kvaddr, uvaddr, spu_addr, ctx_addr;
ulong *ctxs;
ulong list_head[2];
struct list_data list_data, *ld;
/* Walking SPUs */
for (i = 0; i < NR_SPUS; i++) {
- addr = get_spu_addr(spu[i]) + CBE_OFFSET(spu, ctx);
- readmem(addr, KVADDR, &ctx, sizeof(ctx), "show_ctx_info_all",
- FAULT_ON_ERROR);
- show_ctx_info(ctx);
+ spu_addr = get_spu_addr(spu[i]) + CBE_OFFSET(spu, ctx);
+ readmem(spu_addr, KVADDR, &ctx_addr, sizeof(ctx_addr),
+ "show_ctx_info_all", FAULT_ON_ERROR);
+ if (ctx_addr)
+ show_ctx_info(ctx_addr);
}
/* Walking SPU runqueue */
@@ -387,7 +391,7 @@
prio_size = CBE_SIZE(spu_prio_array);
u_spu_prio = (char *)GETBUF(prio_size);
- readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs",
+ readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs",
FAULT_ON_ERROR);
for (i = 0; i < MAX_PRIO; i++) {
@@ -470,7 +474,7 @@
/* Testing for SPU ID */
if ((dvalue >= 0) && (dvalue < NR_SPUS)) {
addr = get_spu_addr(spu[dvalue]) + CBE_OFFSET(spu, ctx);
- readmem(addr, KVADDR, &ctx, sizeof(ctx),
+ readmem(addr, KVADDR, &ctx, sizeof(ctx),
"str_to_spuctx ID", FAULT_ON_ERROR);
type = STR_SPU_ID;
@@ -481,9 +485,9 @@
else {
/* Testing for PID */
for (i = 0; i < NR_SPUS; i++) {
- addr = get_spu_addr(spu[i]) +
+ addr = get_spu_addr(spu[i]) +
CBE_OFFSET(spu, pid);
- readmem(addr, KVADDR, &pid, sizeof(pid),
+ readmem(addr, KVADDR, &pid, sizeof(pid),
"str_to_spuctx PID", FAULT_ON_ERROR);
if (dvalue == pid) {
@@ -506,7 +510,7 @@
/* Testing for spuctx address on SPUs */
for (i = 0; i < NR_SPUS; i++) {
addr = get_spu_addr(spu[i]) + CBE_OFFSET(spu, ctx);
- readmem(addr, KVADDR, &ctx, sizeof(ctx),
+ readmem(addr, KVADDR, &ctx, sizeof(ctx),
"str_to_spuctx CTX", FAULT_ON_ERROR);
if (hvalue == ctx) {
@@ -520,7 +524,7 @@
/* Testing for spuctx address on SPU runqueue */
if (symbol_exists("spu_prio")) {
spu_prio_addr = symbol_value("spu_prio");
- readmem(spu_prio_addr, KVADDR, &k_spu_prio,
+ readmem(spu_prio_addr, KVADDR, &k_spu_prio,
sizeof(k_spu_prio), "runq_array", FAULT_ON_ERROR);
}
else
@@ -532,7 +536,7 @@
prio_size = CBE_SIZE(spu_prio_array);
u_spu_prio = (char *)GETBUF(prio_size);
- readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size,
+ readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size,
"get_runq_ctxs", FAULT_ON_ERROR);
for (i = 0; i < MAX_PRIO; i++) {
@@ -563,7 +567,7 @@
ctxs_size = cnt * sizeof(ulong);
ctxs = (ulong *)GETBUF(ctxs_size);
-
+
BZERO(ctxs, ctxs_size);
cnt = retrieve_list(ctxs, cnt);
hq_close();
@@ -587,8 +591,8 @@
return type;
}
-/*
- * spuctx command stands for "spu context" and shows the context fields
+/*
+ * spuctx command stands for "spu context" and shows the context fields
* for the spu or respective struct address passed as an argument
*/
void cmd_spuctx()
@@ -598,7 +602,7 @@
ulong *ctxlist;
while ((c = getopt(argcnt, args, "")) != EOF) {
- switch(c)
+ switch(c)
{
default:
argerrs++;
@@ -619,7 +623,7 @@
while (args[optind]) {
if (IS_A_NUMBER(args[optind])) {
- switch (str_to_spuctx(args[optind], &value, &ctx))
+ switch (str_to_spuctx(args[optind], &value, &ctx))
{
case STR_SPU_ID:
case STR_SPU_PID:
@@ -634,7 +638,7 @@
}
}
else
- error(INFO, "Invalid SPU reference: %s\n",
+ error(INFO, "Invalid SPU reference: %s\n",
args[optind]);
optind++;
}
@@ -662,39 +666,44 @@
const char *state_str;
if (spu_info) {
- readmem(spu_info + CBE_OFFSET(crash_spu_info,
+ readmem(spu_info + CBE_OFFSET(crash_spu_info,
saved_spu_status_R), KVADDR, &status, sizeof(status),
"print_spu_header: get status", FAULT_ON_ERROR);
size = CBE_SIZE(spu);
spu_data = GETBUF(size);
spu_addr = get_spu_addr(spu_info);
- readmem(spu_addr, KVADDR, spu_data, size, "SPU struct",
+ readmem(spu_addr, KVADDR, spu_data, size, "SPU struct",
FAULT_ON_ERROR);
id = *(int *)(spu_data + CBE_OFFSET(spu, number));
- ctx_addr = *(ulong *)(spu_data + CBE_OFFSET(spu, ctx));
pid = *(int *)(spu_data + CBE_OFFSET(spu, pid));
+ ctx_addr = *(ulong *)(spu_data + CBE_OFFSET(spu, ctx));
- readmem(ctx_addr + CBE_OFFSET(spu_context, state), KVADDR,
- &state, sizeof(state), "print_spu_header get ctxstate",
- FAULT_ON_ERROR);
-
- switch (state) {
- case 0: /* SPU_STATE_RUNNABLE */
- state_str = "RUNNABLE";
- break;
-
- case 1: /* SPU_STATE_SAVED */
- state_str = " SAVED ";
- break;
+ if (ctx_addr) {
+ readmem(ctx_addr + CBE_OFFSET(spu_context, state),
+ KVADDR, &state, sizeof(state),
+ "print_spu_header get ctxstate", FAULT_ON_ERROR);
+
+ switch (state) {
+ case 0: /* SPU_STATE_RUNNABLE */
+ state_str = "RUNNABLE";
+ break;
+
+ case 1: /* SPU_STATE_SAVED */
+ state_str = " SAVED ";
+ break;
- default:
- state_str = "UNKNOWN ";
+ default:
+ state_str = "UNKNOWN ";
+ }
+ }
+ else {
+ state_str = " - ";
}
- fprintf(fp, "%2d %16lx %s %16lx %s %5d\n", id,
- spu_addr,
+ fprintf(fp, "%2d %16lx %s %16lx %s %5d\n", id,
+ spu_addr,
status % 2 ? "RUNNING" : (ctx_addr ? "STOPPED" : " IDLE
"),
ctx_addr, state_str, pid);
@@ -747,7 +756,7 @@
}
/*
- * spus stands for "spu state" and shows what contexts are running in what
+ * spus stands for "spu state" and shows what contexts are running in what
* SPU.
*/
void cmd_spus()
@@ -792,7 +801,7 @@
prio_size = CBE_SIZE(spu_prio_array);
u_spu_prio = (char *)GETBUF(prio_size);
- readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs",
+ readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs",
FAULT_ON_ERROR);
for (i = 0; i < MAX_PRIO; i++) {
@@ -829,7 +838,7 @@
}
/*
- * spurq stands for "spu run queue" and shows info about the contexts
+ * spurq stands for "spu run queue" and shows info about the contexts
* that are on the SPU run queue
*/
void cmd_spurq()
@@ -868,7 +877,7 @@
SPUCTX_CMD_NAME,
"shows complete info about a SPU context",
"[ID | PID | CTXADDR] ...",
-
+
" This command shows the fields of spu and spu_context structs for a ",
"SPU context, including debug info specially saved by kdump after a ",
"crash.",
@@ -948,7 +957,7 @@
" saved_spu_status_R = 1",
" saved_spu_npc_RW = 0",
- "\n Show info about the context whose struct spu_context address is ",
+ "\n Show info about the context whose struct spu_context address is ",
"0xc00000003dcbed80:\n",
"crash> spuctx 0x00000003dcbed80",
" ...",
@@ -995,7 +1004,7 @@
SPURQ_CMD_NAME,
"shows contexts on the SPU runqueue",
" ",
- " This command shows info about all contexts waiting for execution ",
+ " This command shows info about all contexts waiting for execution ",
"in the SPU runqueue. No parameter is needed.",
"\nEXAMPLE",
" Show SPU runqueue:",
------------------------------------------------------------------------
/* spu.c - commands for viewing Cell/B.E. SPUs data
*
* (C) Copyright 2007 IBM Corp.
*
* Author: Lucio Correia <luciojhc(a)br.ibm.com>
*
* 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 NR_SPUS (16) /* Enough for current hardware */
#define MAX_PRIO (140)
#define MAX_PROPERTY_NAME (64)
#define STR_SPU_INVALID (0x0)
#define STR_SPU_ID (0x1)
#define STR_SPU_PID (0x2)
#define STR_SPU_CTX_ADDR (0x8)
#define SPUCTX_CMD_NAME "spuctx"
#define SPUS_CMD_NAME "spus"
#define SPURQ_CMD_NAME "spurq"
struct cbe_size_table;
struct cbe_offset_table;
void init_cbe_size_table(void);
void init_cbe_offset_table(void);
ulong get_spu_addr(ulong spu_info);
void cmd_spus(void);
void cmd_spurq(void);
void cmd_spuctx(void);
char *help_spus[];
char *help_spurq[];
void show_spu_state(ulong spu);
void dump_spu_runq(ulong k_prio_array);
char *help_spuctx[];
void show_ctx_info(ulong ctx_addr);
void print_ctx_info(char *ctx_data, char *spu_data, int info);
void show_ctx_info_all(void);
static struct command_table_entry command_table[] = {
SPUCTX_CMD_NAME, cmd_spuctx, help_spuctx, 0,
SPUS_CMD_NAME, cmd_spus, help_spus, 0,
SPURQ_CMD_NAME, cmd_spurq, help_spurq, 0,
NULL
};
struct cbe_size_table {
long crash_spu_info;
long spu;
long spu_context;
long spu_prio_array;
long list_head;
} cbe_size_table;
struct cbe_offset_table {
long crash_spu_info_spu;
long crash_spu_info_saved_mfc_sr1_RW;
long crash_spu_info_saved_mfc_dar;
long crash_spu_info_saved_mfc_dsisr;
long crash_spu_info_saved_spu_runcntl_RW;
long crash_spu_info_saved_spu_status_R;
long crash_spu_info_saved_spu_npc_RW;
long spu_node;
long spu_number;
long spu_ctx;
long spu_pid;
long spu_name;
long spu_slb_replace;
long spu_mm;
long spu_timestamp;
long spu_class_0_pending;
long spu_problem;
long spu_priv2;
long spu_flags;
long spu_context_spu;
long spu_context_state;
long spu_context_prio;
long spu_context_local_store;
long spu_context_rq;
long spu_prio_array_runq;
} cbe_offset_table;
#define CBE_SIZE(X) (cbe_size_table.X)
#define CBE_OFFSET(X, Y) (cbe_offset_table.X##_##Y)
#define CBE_SIZE_INIT(X, Y) \
do { \
cbe_size_table.X = STRUCT_SIZE(Y); \
if (cbe_size_table.X == -1) \
error(FATAL, "Couldn't get %s size.\n", Y); \
} while(0)
#define CBE_OFFSET_INIT(X, Y, Z) \
do { \
cbe_offset_table.X = MEMBER_OFFSET(Y, Z); \
if (cbe_offset_table.X == -1) \
error(FATAL, "Couldn't get %s.%s offset.\n", Y, Z); \
} while(0)
ulong spu[NR_SPUS];
/*****************************************************************************
* INIT FUNCTIONS
*/
/*
* Read kernel virtual addresses of crash_spu_info data stored by kdump
*/
void init_cbe_size_table(void)
{
CBE_SIZE_INIT(crash_spu_info, "crash_spu_info");
CBE_SIZE_INIT(spu, "spu");
CBE_SIZE_INIT(spu_context, "spu_context");
CBE_SIZE_INIT(spu_prio_array, "spu_prio_array");
CBE_SIZE_INIT(list_head, "list_head");
}
void init_cbe_offset_table(void)
{
CBE_OFFSET_INIT(crash_spu_info_spu, "crash_spu_info", "spu");
CBE_OFFSET_INIT(crash_spu_info_saved_mfc_sr1_RW, "crash_spu_info",
"saved_mfc_sr1_RW");
CBE_OFFSET_INIT(crash_spu_info_saved_mfc_dar, "crash_spu_info",
"saved_mfc_dar");
CBE_OFFSET_INIT(crash_spu_info_saved_mfc_dsisr, "crash_spu_info",
"saved_mfc_dsisr");
CBE_OFFSET_INIT(crash_spu_info_saved_spu_runcntl_RW, "crash_spu_info",
"saved_spu_runcntl_RW");
CBE_OFFSET_INIT(crash_spu_info_saved_spu_status_R, "crash_spu_info",
"saved_spu_status_R");
CBE_OFFSET_INIT(crash_spu_info_saved_spu_npc_RW, "crash_spu_info",
"saved_spu_npc_RW");
CBE_OFFSET_INIT(spu_node, "spu", "node");
CBE_OFFSET_INIT(spu_number, "spu", "number");
CBE_OFFSET_INIT(spu_ctx, "spu", "ctx");
CBE_OFFSET_INIT(spu_pid, "spu", "pid");
CBE_OFFSET_INIT(spu_name, "spu", "name");
CBE_OFFSET_INIT(spu_slb_replace, "spu", "slb_replace");
CBE_OFFSET_INIT(spu_mm, "spu", "mm");
CBE_OFFSET_INIT(spu_timestamp, "spu", "timestamp");
CBE_OFFSET_INIT(spu_class_0_pending, "spu", "class_0_pending");
CBE_OFFSET_INIT(spu_problem, "spu", "problem");
CBE_OFFSET_INIT(spu_priv2, "spu", "priv2");
CBE_OFFSET_INIT(spu_flags, "spu", "flags");
CBE_OFFSET_INIT(spu_context_spu, "spu_context", "spu");
CBE_OFFSET_INIT(spu_context_state, "spu_context", "state");
CBE_OFFSET_INIT(spu_context_prio, "spu_context", "prio");
CBE_OFFSET_INIT(spu_context_local_store, "spu_context",
"local_store");
CBE_OFFSET_INIT(spu_context_rq, "spu_context", "rq");
CBE_OFFSET_INIT(spu_prio_array_runq, "spu_prio_array", "runq");
}
void get_crash_spu_info(void)
{
int i;
ulong addr;
long struct_size;
addr = symbol_value("crash_spu_info");
struct_size = CBE_SIZE(crash_spu_info);
for (i = 0; i < NR_SPUS; i++)
spu[i] = addr + (i * struct_size);
}
_init()
{
int i, n_registered;
struct command_table_entry *cte;
init_cbe_size_table();
init_cbe_offset_table();
for (i = 0; i < NR_SPUS; i++)
spu[i] = 0;
register_extension(command_table);
get_crash_spu_info();
}
_fini() { }
/*****************************************************************************
* BASIC FUNCTIONS
*/
/*
* Returns a pointer to the requested SPU field
*/
ulong get_spu_addr(ulong spu_info)
{
ulong spu_addr;
readmem(spu_info + CBE_OFFSET(crash_spu_info, spu), KVADDR, &spu_addr,
sizeof(spu_addr), "get_spu_addr", FAULT_ON_ERROR);
return spu_addr;
}
/*****************************************************************************
* SPUCTX COMMAND
*/
#define DUMP_WIDTH 23
#define DUMP_SPU_NAME \
do { \
fprintf(fp, " %-*s = %s\n", DUMP_WIDTH, "name", name_str); \
} while(0)
#define DUMP_SPU_FIELD(format, field, cast) \
do { \
offset = CBE_OFFSET(spu, field); \
fprintf(fp, " %-*s = "format"\n", DUMP_WIDTH, #field, \
cast(spu_data + offset)); \
} while(0)
#define DUMP_CTX_FIELD(format, field, cast) \
do { \
offset = CBE_OFFSET(spu_context, field); \
fprintf(fp, " %-*s = "format"\n", DUMP_WIDTH, #field, \
cast(ctx_data + offset)); \
} while(0)
#define DUMP_DBG_FIELD(format, field, cast) \
do { \
offset = CBE_OFFSET(crash_spu_info, field); \
fprintf(fp, " %-*s = "format"\n", DUMP_WIDTH, #field, \
cast(debug_data + offset)); \
} while(0)
/*
* Print the spu and spu_context structs fields. Some SPU memory-mapped IO
* registers are taken directly from crash_spu_info.
*/
void print_ctx_info(char *ctx_data, char *spu_data, int info)
{
long offset, size;
char *name_str, *debug_data;
DUMP_CTX_FIELD("%d", state, *(int *));
DUMP_CTX_FIELD("%d", prio, *(int *));
DUMP_CTX_FIELD("%p", local_store, *(ulong *));
DUMP_CTX_FIELD("%p", rq, *(ulong *));
if (spu_data) {
offset = CBE_OFFSET(spu, name);
size = MAX_PROPERTY_NAME * sizeof(char);
name_str = (char *)GETBUF(size);
readmem(*(ulong *)(spu_data + offset), KVADDR, name_str, size,
"name_str", FAULT_ON_ERROR);
DUMP_SPU_NAME;
FREEBUF(name_str);
DUMP_SPU_FIELD("%d", node, *(int *));
DUMP_SPU_FIELD("%d", number, *(int *));
DUMP_SPU_FIELD("%d", pid, *(int *));
DUMP_SPU_FIELD("0x%x", slb_replace, *(unsigned int *));
DUMP_SPU_FIELD("%p", mm, *(ulong *));
DUMP_SPU_FIELD("%p", timestamp, *(long long *));
DUMP_SPU_FIELD("%d", class_0_pending, *(int *));
DUMP_SPU_FIELD("%p", problem, *(ulong *));
DUMP_SPU_FIELD("%p", priv2, *(ulong *));
DUMP_SPU_FIELD("0x%lx", flags, *(ulong *));
size = CBE_SIZE(crash_spu_info);
debug_data = (char *)GETBUF(size);
readmem(spu[info], KVADDR, debug_data, size, "debug_data",
FAULT_ON_ERROR);
DUMP_DBG_FIELD("0x%lx", saved_mfc_sr1_RW, *(ulong *));
DUMP_DBG_FIELD("0x%lx", saved_mfc_dar, *(ulong *));
DUMP_DBG_FIELD("0x%lx", saved_mfc_dsisr, *(ulong *));
DUMP_DBG_FIELD("0x%x", saved_spu_runcntl_RW, *(uint *));
DUMP_DBG_FIELD("0x%x", saved_spu_status_R, *(uint *));
DUMP_DBG_FIELD("0x%x", saved_spu_npc_RW, *(uint *));
FREEBUF(debug_data);
}
}
/*
* Pass ctx and respective spu data to print_ctx_info for the contexts in
* ctx_addr list (chosen contexts).
*/
void show_ctx_info(ulong ctx_addr)
{
int number, info, i;
char *ctx_data, *spu_data;
long size, offset;
ulong spu_addr, addr;
if (!ctx_addr)
return;
spu_data = NULL;
info = 0;
size = CBE_SIZE(spu_context);
ctx_data = GETBUF(size);
if (!ctx_data)
error(FATAL, "Couldn't allocate memory for ctx.\n");
readmem(ctx_addr, KVADDR, ctx_data, size, "show_ctx_info ctx",
FAULT_ON_ERROR);
spu_addr = *(ulong *)(ctx_data + CBE_OFFSET(spu_context, spu));
if (spu_addr) {
size = CBE_SIZE(spu);
spu_data = GETBUF(size);
if (!spu_data)
error(FATAL, "Couldn't allocate memory for spu.\n");
readmem(spu_addr, KVADDR, spu_data, size, "show_ctx_info spu",
FAULT_ON_ERROR);
for (i = 0; i < NR_SPUS; i++) {
readmem(spu[i], KVADDR, &addr, sizeof(addr), "spu addr",
FAULT_ON_ERROR);
if (addr == spu_addr)
info = i;
}
}
fprintf(fp,"\nDumping context fields for spu_context %lx:\n", ctx_addr);
print_ctx_info(ctx_data, spu_data, info);
FREEBUF(ctx_data);
if (spu_addr)
FREEBUF(spu_data);
}
/*
* Pass ctx and respective spu data to show_ctx_info for all the contexts
* running and on the runqueue.
*/
void show_ctx_info_all(void)
{
int i, j, cnt;
long prio_size, prio_runq_off, ctx_rq_off, jump, offset, ctxs_size;
char *u_spu_prio;
ulong spu_prio_addr, k_spu_prio, kvaddr, uvaddr, spu_addr, ctx_addr;
ulong *ctxs;
ulong list_head[2];
struct list_data list_data, *ld;
/* Walking SPUs */
for (i = 0; i < NR_SPUS; i++) {
spu_addr = get_spu_addr(spu[i]) + CBE_OFFSET(spu, ctx);
readmem(spu_addr, KVADDR, &ctx_addr, sizeof(ctx_addr),
"show_ctx_info_all", FAULT_ON_ERROR);
if (ctx_addr)
show_ctx_info(ctx_addr);
}
/* Walking SPU runqueue */
if (symbol_exists("spu_prio")) {
spu_prio_addr = symbol_value("spu_prio");
readmem(spu_prio_addr, KVADDR, &k_spu_prio, sizeof(k_spu_prio),
"runq_array", FAULT_ON_ERROR);
}
else
error(FATAL, "Could not get SPU run queue data.\n");
jump = CBE_SIZE(list_head);
prio_runq_off = CBE_OFFSET(spu_prio_array, runq);
ctx_rq_off = CBE_OFFSET(spu_context, rq);
prio_size = CBE_SIZE(spu_prio_array);
u_spu_prio = (char *)GETBUF(prio_size);
readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs",
FAULT_ON_ERROR);
for (i = 0; i < MAX_PRIO; i++) {
offset = prio_runq_off + i * jump;
kvaddr = k_spu_prio + offset;
uvaddr = (ulong)u_spu_prio + offset;
BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2);
if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr))
continue;
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->start = list_head[0];
ld->list_head_offset = ctx_rq_off;
ld->flags |= RETURN_ON_LIST_ERROR;
ld->end = kvaddr;
hq_open();
cnt = do_list(ld);
if (cnt == -1) {
hq_close();
FREEBUF(u_spu_prio);
error(FATAL, "Couldn't walk the list.\n");
}
ctxs_size = cnt * sizeof(ulong);
ctxs = (ulong *)GETBUF(ctxs_size);
BZERO(ctxs, ctxs_size);
cnt = retrieve_list(ctxs, cnt);
hq_close();
for (j = 0; j < cnt; j++)
show_ctx_info(ctxs[j]);
FREEBUF(ctxs);
}
FREEBUF(u_spu_prio);
}
/*
* Tries to discover the meaning of string and to find the referred context
*/
int str_to_spuctx(char *string, ulong *value, ulong *spu_ctx)
{
char *s, *u_spu_prio;
ulong dvalue, hvalue, addr, ctx;
ulong k_spu_prio, spu_prio_addr, kvaddr, uvaddr;
int type, pid, i, j, cnt;
long prio_size, prio_runq_off, ctx_rq_off, jump, offset, ctxs_size;
ulong *ctxs;
ulong list_head[2];
struct list_data list_data, *ld;
if (string == NULL) {
error(INFO, "%s: received NULL string.\n", __FUNCTION__);
return STR_SPU_INVALID;
}
s = string;
dvalue = hvalue = BADADDR;
if (decimal(s, 0))
dvalue = dtol(s, RETURN_ON_ERROR, NULL);
if (hexadecimal(s, 0)) {
if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
s += 2;
if (strlen(s) <= MAX_HEXADDR_STRLEN)
hvalue = htol(s, RETURN_ON_ERROR, NULL);
}
type = STR_SPU_INVALID;
if (dvalue != BADADDR) {
/* Testing for SPU ID */
if ((dvalue >= 0) && (dvalue < NR_SPUS)) {
addr = get_spu_addr(spu[dvalue]) + CBE_OFFSET(spu, ctx);
readmem(addr, KVADDR, &ctx, sizeof(ctx),
"str_to_spuctx ID", FAULT_ON_ERROR);
type = STR_SPU_ID;
*value = dvalue;
*spu_ctx = ctx;
return type;
}
else {
/* Testing for PID */
for (i = 0; i < NR_SPUS; i++) {
addr = get_spu_addr(spu[i]) +
CBE_OFFSET(spu, pid);
readmem(addr, KVADDR, &pid, sizeof(pid),
"str_to_spuctx PID", FAULT_ON_ERROR);
if (dvalue == pid) {
addr = get_spu_addr(spu[i]) +
CBE_OFFSET(spu, ctx);
readmem(addr, KVADDR, &ctx, sizeof(ctx),
"str_to_spuctx PID ctx",
FAULT_ON_ERROR);
type = STR_SPU_PID;
*value = dvalue;
*spu_ctx = ctx;
return type;
}
}
}
}
if (hvalue != BADADDR) {
/* Testing for spuctx address on SPUs */
for (i = 0; i < NR_SPUS; i++) {
addr = get_spu_addr(spu[i]) + CBE_OFFSET(spu, ctx);
readmem(addr, KVADDR, &ctx, sizeof(ctx),
"str_to_spuctx CTX", FAULT_ON_ERROR);
if (hvalue == ctx) {
type = STR_SPU_CTX_ADDR;
*value = hvalue;
*spu_ctx = ctx;
return type;
}
}
/* Testing for spuctx address on SPU runqueue */
if (symbol_exists("spu_prio")) {
spu_prio_addr = symbol_value("spu_prio");
readmem(spu_prio_addr, KVADDR, &k_spu_prio,
sizeof(k_spu_prio), "runq_array", FAULT_ON_ERROR);
}
else
error(FATAL, "Could not get SPU run queue data.\n");
jump = CBE_SIZE(list_head);
prio_runq_off = CBE_OFFSET(spu_prio_array, runq);
ctx_rq_off = CBE_OFFSET(spu_context, rq);
prio_size = CBE_SIZE(spu_prio_array);
u_spu_prio = (char *)GETBUF(prio_size);
readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size,
"get_runq_ctxs", FAULT_ON_ERROR);
for (i = 0; i < MAX_PRIO; i++) {
offset = prio_runq_off + i * jump;
kvaddr = k_spu_prio + offset;
uvaddr = (ulong)u_spu_prio + offset;
BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2);
if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr))
continue;
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->start = list_head[0];
ld->list_head_offset = ctx_rq_off;
ld->flags |= RETURN_ON_LIST_ERROR;
ld->end = kvaddr;
hq_open();
cnt = do_list(ld);
if (cnt == -1) {
hq_close();
FREEBUF(u_spu_prio);
error(FATAL, "Couldn't walk the list.\n");
}
ctxs_size = cnt * sizeof(ulong);
ctxs = (ulong *)GETBUF(ctxs_size);
BZERO(ctxs, ctxs_size);
cnt = retrieve_list(ctxs, cnt);
hq_close();
for (j = 0; j < cnt; j++)
if (hvalue == ctxs[j]) {
type = STR_SPU_CTX_ADDR;
*value = hvalue;
*spu_ctx = ctxs[j];
FREEBUF(u_spu_prio);
FREEBUF(ctxs);
return type;
}
FREEBUF(ctxs);
}
FREEBUF(u_spu_prio);
}
return type;
}
/*
* spuctx command stands for "spu context" and shows the context fields
* for the spu or respective struct address passed as an argument
*/
void cmd_spuctx()
{
int i, c, cnt;
ulong value, ctx;
ulong *ctxlist;
while ((c = getopt(argcnt, args, "")) != EOF) {
switch(c)
{
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (!args[optind]) {
show_ctx_info_all();
return;
}
cnt = 0;
ctxlist = (ulong *)GETBUF((MAXARGS+NR_CPUS)*sizeof(ctx));
while (args[optind]) {
if (IS_A_NUMBER(args[optind])) {
switch (str_to_spuctx(args[optind], &value, &ctx))
{
case STR_SPU_ID:
case STR_SPU_PID:
case STR_SPU_CTX_ADDR:
ctxlist[cnt++] = ctx;
break;
case STR_SPU_INVALID:
error(INFO, "Invalid SPU reference: %s\n",
args[optind]);
break;
}
}
else
error(INFO, "Invalid SPU reference: %s\n",
args[optind]);
optind++;
}
if (cnt == 0)
error(INFO, "No valid ID, PID or context address.\n");
else
for (i = 0; i < cnt; i++)
show_ctx_info(ctxlist[i]);
FREEBUF(ctxlist);
}
/*****************************************************************************
* SPUS COMMAND
*/
void print_spu_header(ulong spu_info)
{
int id, pid, size, state;
uint status;
ulong ctx_addr, spu_addr;
char *spu_data;
const char *state_str;
if (spu_info) {
readmem(spu_info + CBE_OFFSET(crash_spu_info,
saved_spu_status_R), KVADDR, &status, sizeof(status),
"print_spu_header: get status", FAULT_ON_ERROR);
size = CBE_SIZE(spu);
spu_data = GETBUF(size);
spu_addr = get_spu_addr(spu_info);
readmem(spu_addr, KVADDR, spu_data, size, "SPU struct",
FAULT_ON_ERROR);
id = *(int *)(spu_data + CBE_OFFSET(spu, number));
pid = *(int *)(spu_data + CBE_OFFSET(spu, pid));
ctx_addr = *(ulong *)(spu_data + CBE_OFFSET(spu, ctx));
if (ctx_addr) {
readmem(ctx_addr + CBE_OFFSET(spu_context, state),
KVADDR, &state, sizeof(state),
"print_spu_header get ctxstate", FAULT_ON_ERROR);
switch (state) {
case 0: /* SPU_STATE_RUNNABLE */
state_str = "RUNNABLE";
break;
case 1: /* SPU_STATE_SAVED */
state_str = " SAVED ";
break;
default:
state_str = "UNKNOWN ";
}
}
else {
state_str = " - ";
}
fprintf(fp, "%2d %16lx %s %16lx %s %5d\n", id,
spu_addr,
status % 2 ? "RUNNING" : (ctx_addr ? "STOPPED" : " IDLE
"),
ctx_addr, state_str, pid);
FREEBUF(spu_data);
}
}
void print_node_header(int node)
{
fprintf(fp, "\n");
fprintf(fp, "NODE %i:\n", node);
fprintf(fp, "ID SPUADDR SPUSTATUS CTXADDR \
CTXSTATE PID \n");
}
void show_spus()
{
int i, j, nr_cpus, show_header, node;
ulong spu_addr, addr;
long offset;
nr_cpus = kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS;
for (i = 0; i < nr_cpus; i++) {
show_header = TRUE;
for (j = 0; j < NR_SPUS; j++) {
addr = spu[j] + CBE_OFFSET(crash_spu_info, spu);
readmem(addr, KVADDR, &spu_addr, sizeof(spu_addr),
"show_spus spu_addr", FAULT_ON_ERROR);
offset = CBE_OFFSET(spu, node);
if (offset == -1)
error(FATAL, "Couldn't get spu.node offset.\n");
spu_addr += offset;
readmem(spu_addr, KVADDR, &node, sizeof(node),
"show_spus node", FAULT_ON_ERROR);
if (node == i) {
if (show_header) {
print_node_header(node);
show_header = FALSE;
}
print_spu_header(spu[j]);
}
}
}
}
/*
* spus stands for "spu state" and shows what contexts are running in what
* SPU.
*/
void cmd_spus()
{
int c;
while ((c = getopt(argcnt, args, "")) != EOF) {
switch(c)
{
default:
argerrs++;
break;
}
}
if (argerrs || args[optind])
cmd_usage(pc->curcmd, SYNOPSIS);
else
show_spus();
}
/*****************************************************************************
* SPURQ COMMAND
*/
/*
* Prints the addresses of SPU contexts on the SPU runqueue.
*/
void dump_spu_runq(ulong k_spu_prio)
{
int i, cnt;
long prio_size, prio_runq_off, ctx_rq_off, jump, offset;
char *u_spu_prio;
ulong kvaddr, uvaddr;
ulong list_head[2];
struct list_data list_data, *ld;
prio_runq_off = CBE_OFFSET(spu_prio_array, runq);
jump = CBE_SIZE(list_head);
ctx_rq_off = CBE_OFFSET(spu_context, rq);
prio_size = CBE_SIZE(spu_prio_array);
u_spu_prio = (char *)GETBUF(prio_size);
readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs",
FAULT_ON_ERROR);
for (i = 0; i < MAX_PRIO; i++) {
offset = prio_runq_off + (i * jump);
kvaddr = k_spu_prio + offset;
uvaddr = (ulong)u_spu_prio + offset;
BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2);
if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr))
continue;
fprintf(fp, "PRIO[%i]:\n", i);
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->start = list_head[0];
ld->list_head_offset = ctx_rq_off;
ld->flags |= VERBOSE;
ld->end = kvaddr;
hq_open();
cnt = do_list(ld);
hq_close();
if (cnt == -1) {
FREEBUF(u_spu_prio);
error(FATAL, "Couldn't walk runqueue[%i].\n", i);
}
}
FREEBUF(u_spu_prio);
}
/*
* spurq stands for "spu run queue" and shows info about the contexts
* that are on the SPU run queue
*/
void cmd_spurq()
{
int c;
ulong spu_prio_addr, spu_prio;
long size;
while ((c = getopt(argcnt, args, "")) != EOF) {
switch(c)
{
default:
argerrs++;
break;
}
}
if (argerrs || args[optind])
cmd_usage(pc->curcmd, SYNOPSIS);
else {
if (symbol_exists("spu_prio")) {
spu_prio_addr = symbol_value("spu_prio");
readmem(spu_prio_addr, KVADDR, &spu_prio,
sizeof(spu_prio), "runq_array", FAULT_ON_ERROR);
dump_spu_runq(spu_prio);
} else
error(FATAL, "Could not get SPU run queue data.\n");
}
}
/**********************************************************************************
* HELP TEXTS
*/
char *help_spuctx[] = {
SPUCTX_CMD_NAME,
"shows complete info about a SPU context",
"[ID | PID | CTXADDR] ...",
" This command shows the fields of spu and spu_context structs for a ",
"SPU context, including debug info specially saved by kdump after a ",
"crash.",
" By default, it shows info about all the contexts created by the ",
"system, including ones in the runqueue. To specify the contexts of ",
"interest, the PID of the controller task, ID of the SPU which the ",
"context is bound to or the address of spu_context struct can be used ",
"as parameters.",
"\nEXAMPLES",
"\n Show info about contexts bound to SPUs 0 and 7, and the one ",
"controlled by thread whose PID is 1524:",
"\n crash> spuctx 0 7 1524",
"\n Dumping context fields for spu_context c00000003dcbdd80:",
" state = 0",
" prio = 120",
" local_store = 0xc000000039055840",
" rq = 0xc00000003dcbe720",
" node = 0",
" number = 0",
" pid = 1524",
" name = spe",
" slb_replace = 0",
" mm = 0xc0000000005dd700",
" timestamp = 0x10000566f",
" class_0_pending = 0",
" problem = 0xd000080080210000",
" priv2 = 0xd000080080230000",
" flags = 0",
" saved_mfc_sr1_RW = 59",
" saved_mfc_dar = 14987979559889612800",
" saved_mfc_dsisr = 0",
" saved_spu_runcntl_RW = 1",
" saved_spu_status_R = 1",
" saved_spu_npc_RW = 0",
"\n Dumping context fields for spu_context c00000003dec4e80:",
" state = 0",
" prio = 120",
" local_store = 0xc00000003b1cea40",
" rq = 0xc00000003dec5820",
" node = 0",
" number = 7",
" pid = 1538",
" name = spe",
" slb_replace = 0",
" mm = 0xc0000000005d2b80",
" timestamp = 0x10000566f",
" class_0_pending = 0",
" problem = 0xd000080080600000",
" priv2 = 0xd000080080620000",
" flags = 0",
" saved_mfc_sr1_RW = 59",
" saved_mfc_dar = 14987979559896297472",
" saved_mfc_dsisr = 0",
" saved_spu_runcntl_RW = 1",
" saved_spu_status_R = 1",
" saved_spu_npc_RW = 0",
"\n Dumping context fields for spu_context c00000003dcbdd80:",
" state = 0",
" prio = 120",
" local_store = 0xc000000039055840",
" rq = 0xc00000003dcbe720",
" node = 0",
" number = 0",
" pid = 1524",
" name = spe",
" slb_replace = 0",
" mm = 0xc0000000005dd700",
" timestamp = 0x10000566f",
" class_0_pending = 0",
" problem = 0xd000080080210000",
" priv2 = 0xd000080080230000",
" flags = 0",
" saved_mfc_sr1_RW = 59",
" saved_mfc_dar = 14987979559889612800",
" saved_mfc_dsisr = 0",
" saved_spu_runcntl_RW = 1",
" saved_spu_status_R = 1",
" saved_spu_npc_RW = 0",
"\n Show info about the context whose struct spu_context address is ",
"0xc00000003dcbed80:\n",
"crash> spuctx 0x00000003dcbed80",
" ...",
NULL
};
char *help_spus[] = {
SPUS_CMD_NAME,
"shows how contexts are scheduled in the SPUs",
" ",
" This command shows how the contexts are scheduled in the SPUs of ",
"each node. It provides info about the spu address, SPU status, the ",
"spu_context address, context state and spu_context addresses and the ",
"PID of controller thread for each SPU.",
"\nEXAMPLE",
" Show SPU contexts:",
"\n crash> spus",
" NODE 0:",
" ID SPUADDR SPUSTATUS CTXADDR CTXSTATE PID ",
" 0 c000000001fac880 RUNNING c00000003dcbdd80 RUNNABLE 1524",
" 1 c000000001faca80 RUNNING c00000003bf34e00 RUNNABLE 1528",
" 2 c000000001facc80 RUNNING c00000003bf30e00 RUNNABLE 1525",
" 3 c000000001face80 RUNNING c000000039421d00 RUNNABLE 1533",
" 4 c00000003ee29080 RUNNING c00000003dec3e80 RUNNABLE 1534",
" 5 c00000003ee28e80 RUNNING c00000003bf32e00 RUNNABLE 1526",
" 6 c00000003ee28c80 STOPPED c000000039e5e700 SAVED 1522",
" 7 c00000003ee2e080 RUNNING c00000003dec4e80 RUNNABLE 1538",
"\n NODE 1:",
" ID SPUADDR SPUSTATUS CTXADDR CTXSTATE PID ",
" 8 c00000003ee2de80 RUNNING c00000003dcbed80 RUNNABLE 1529",
" 9 c00000003ee2dc80 RUNNING c00000003bf39e00 RUNNABLE 1535",
" 10 c00000003ee2da80 RUNNING c00000003bf3be00 RUNNABLE 1521",
" 11 c000000001fad080 RUNNING c000000039420d00 RUNNABLE 1532",
" 12 c000000001fad280 RUNNING c00000003bf3ee00 RUNNABLE 1536",
" 13 c000000001fad480 RUNNING c00000003dec2e80 RUNNABLE 1539",
" 14 c000000001fad680 RUNNING c00000003bf3ce00 RUNNABLE 1537",
" 15 c000000001fad880 RUNNING c00000003dec6e80 RUNNABLE 1540",
NULL
};
char *help_spurq[] = {
SPURQ_CMD_NAME,
"shows contexts on the SPU runqueue",
" ",
" This command shows info about all contexts waiting for execution ",
"in the SPU runqueue. No parameter is needed.",
"\nEXAMPLE",
" Show SPU runqueue:",
"\n crash> spurq",
" PRIO[120]:",
" c000000000fd7380",
" c00000003bf31e00",
" PRIO[125]:",
" c000000039422d00",
" c00000000181eb80",
NULL
};