mirror of
https://github.com/crash-utility/crash
synced 2025-02-21 16:06:49 +00:00
Fix to support Linux 4.15 and later kernels that contain kernel
commit e8cfbc245e24887e3c30235f71e9e9405e0cfc39, titled "pid: remove pidhash". The kernel's traditional usage of a pid_hash[] array to store PIDs has been replaced by an IDR radix tree, requiring a new crash plug-in function to gather the system's task set. Without the patch, the crash session fails during initialization with the error message "crash: cannot resolve init_task_union". (anderson@redhat.com)
This commit is contained in:
parent
03a3e57b9a
commit
494a796e11
6
defs.h
6
defs.h
@ -845,6 +845,8 @@ struct task_table { /* kernel/local task table data */
|
||||
long anonpages;
|
||||
ulong stack_end_magic;
|
||||
ulong pf_kthread;
|
||||
ulong pid_radix_tree;
|
||||
int callbacks;
|
||||
};
|
||||
|
||||
#define TASK_INIT_DONE (0x1)
|
||||
@ -864,6 +866,7 @@ struct task_table { /* kernel/local task table data */
|
||||
#define ACTIVE_ONLY (0x4000)
|
||||
#define START_TIME_NSECS (0x8000)
|
||||
#define THREAD_INFO_IN_TASK (0x10000)
|
||||
#define PID_RADIX_TREE (0x20000)
|
||||
|
||||
#define TASK_SLUSH (20)
|
||||
|
||||
@ -1996,6 +1999,8 @@ struct offset_table { /* stash of commonly-used offsets */
|
||||
long mod_arch_specific_orc_unwind;
|
||||
long task_struct_policy;
|
||||
long kmem_cache_random;
|
||||
long pid_namespace_idr;
|
||||
long idr_idr_rt;
|
||||
};
|
||||
|
||||
struct size_table { /* stash of commonly-used sizes */
|
||||
@ -2146,6 +2151,7 @@ struct size_table { /* stash of commonly-used sizes */
|
||||
long sk_buff_len;
|
||||
long orc_entry;
|
||||
long task_struct_policy;
|
||||
long pid;
|
||||
};
|
||||
|
||||
struct array_table {
|
||||
|
32
filesys.c
32
filesys.c
@ -2083,38 +2083,6 @@ vfs_init(void)
|
||||
if (!(ft->inode_cache = (char *)malloc(SIZE(inode)*INODE_CACHE)))
|
||||
error(FATAL, "cannot malloc inode cache\n");
|
||||
|
||||
if (symbol_exists("height_to_maxindex") ||
|
||||
symbol_exists("height_to_maxnodes")) {
|
||||
int newver = symbol_exists("height_to_maxnodes");
|
||||
int tmp ATTRIBUTE_UNUSED;
|
||||
if (!newver) {
|
||||
if (LKCD_KERNTYPES())
|
||||
ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxindex",
|
||||
"radix_tree_preload.nodes", NULL, 0);
|
||||
else
|
||||
ARRAY_LENGTH_INIT(tmp, height_to_maxindex,
|
||||
"height_to_maxindex", NULL, 0);
|
||||
} else {
|
||||
if (LKCD_KERNTYPES())
|
||||
ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxnodes",
|
||||
"radix_tree_preload.nodes", NULL, 0);
|
||||
else
|
||||
ARRAY_LENGTH_INIT(tmp, height_to_maxnodes,
|
||||
"height_to_maxnodes", NULL, 0);
|
||||
}
|
||||
STRUCT_SIZE_INIT(radix_tree_root, "radix_tree_root");
|
||||
STRUCT_SIZE_INIT(radix_tree_node, "radix_tree_node");
|
||||
MEMBER_OFFSET_INIT(radix_tree_root_height,
|
||||
"radix_tree_root","height");
|
||||
MEMBER_OFFSET_INIT(radix_tree_root_rnode,
|
||||
"radix_tree_root","rnode");
|
||||
MEMBER_OFFSET_INIT(radix_tree_node_slots,
|
||||
"radix_tree_node","slots");
|
||||
MEMBER_OFFSET_INIT(radix_tree_node_height,
|
||||
"radix_tree_node","height");
|
||||
MEMBER_OFFSET_INIT(radix_tree_node_shift,
|
||||
"radix_tree_node","shift");
|
||||
}
|
||||
MEMBER_OFFSET_INIT(rb_root_rb_node,
|
||||
"rb_root","rb_node");
|
||||
MEMBER_OFFSET_INIT(rb_node_rb_left,
|
||||
|
@ -8608,6 +8608,10 @@ dump_offset_table(char *spec, ulong makestruct)
|
||||
fprintf(fp, " mnt_namespace_list: %ld\n",
|
||||
OFFSET(mnt_namespace_list));
|
||||
|
||||
fprintf(fp, " pid_namespace_idr: %ld\n",
|
||||
OFFSET(pid_namespace_idr));
|
||||
fprintf(fp, " idr_idr_rt: %ld\n",
|
||||
OFFSET(idr_idr_rt));
|
||||
fprintf(fp, " pid_link_pid: %ld\n",
|
||||
OFFSET(pid_link_pid));
|
||||
fprintf(fp, " pid_hash_chain: %ld\n",
|
||||
@ -10349,6 +10353,8 @@ dump_offset_table(char *spec, ulong makestruct)
|
||||
SIZE(pid_link));
|
||||
fprintf(fp, " upid: %ld\n",
|
||||
SIZE(upid));
|
||||
fprintf(fp, " pid: %ld\n",
|
||||
SIZE(pid));
|
||||
fprintf(fp, " unwind_table: %ld\n",
|
||||
SIZE(unwind_table));
|
||||
fprintf(fp, " rlimit: %ld\n",
|
||||
|
281
task.c
281
task.c
@ -30,6 +30,8 @@ static void refresh_hlist_task_table(void);
|
||||
static void refresh_hlist_task_table_v2(void);
|
||||
static void refresh_hlist_task_table_v3(void);
|
||||
static void refresh_active_task_table(void);
|
||||
static int radix_tree_task_callback(ulong);
|
||||
static void refresh_radix_tree_task_table(void);
|
||||
static struct task_context *store_context(struct task_context *, ulong, char *);
|
||||
static void refresh_context(ulong, ulong);
|
||||
static ulong parent_of(ulong);
|
||||
@ -439,12 +441,55 @@ task_init(void)
|
||||
((len = SIZE(thread_union)) != STACKSIZE()))
|
||||
machdep->stacksize = len;
|
||||
|
||||
MEMBER_OFFSET_INIT(pid_namespace_idr, "pid_namespace", "idr");
|
||||
MEMBER_OFFSET_INIT(idr_idr_rt, "idr", "idr_rt");
|
||||
|
||||
if (symbol_exists("height_to_maxindex") ||
|
||||
symbol_exists("height_to_maxnodes")) {
|
||||
int newver = symbol_exists("height_to_maxnodes");
|
||||
int tmp ATTRIBUTE_UNUSED;
|
||||
if (!newver) {
|
||||
if (LKCD_KERNTYPES())
|
||||
ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxindex",
|
||||
"radix_tree_preload.nodes", NULL, 0);
|
||||
else
|
||||
ARRAY_LENGTH_INIT(tmp, height_to_maxindex,
|
||||
"height_to_maxindex", NULL, 0);
|
||||
} else {
|
||||
if (LKCD_KERNTYPES())
|
||||
ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxnodes",
|
||||
"radix_tree_preload.nodes", NULL, 0);
|
||||
else
|
||||
ARRAY_LENGTH_INIT(tmp, height_to_maxnodes,
|
||||
"height_to_maxnodes", NULL, 0);
|
||||
}
|
||||
STRUCT_SIZE_INIT(radix_tree_root, "radix_tree_root");
|
||||
STRUCT_SIZE_INIT(radix_tree_node, "radix_tree_node");
|
||||
MEMBER_OFFSET_INIT(radix_tree_root_height,
|
||||
"radix_tree_root","height");
|
||||
MEMBER_OFFSET_INIT(radix_tree_root_rnode,
|
||||
"radix_tree_root","rnode");
|
||||
MEMBER_OFFSET_INIT(radix_tree_node_slots,
|
||||
"radix_tree_node","slots");
|
||||
MEMBER_OFFSET_INIT(radix_tree_node_height,
|
||||
"radix_tree_node","height");
|
||||
MEMBER_OFFSET_INIT(radix_tree_node_shift,
|
||||
"radix_tree_node","shift");
|
||||
}
|
||||
|
||||
if (symbol_exists("pidhash") && symbol_exists("pid_hash") &&
|
||||
!symbol_exists("pidhash_shift"))
|
||||
error(FATAL,
|
||||
"pidhash and pid_hash both exist -- cannot distinquish between them\n");
|
||||
|
||||
if (symbol_exists("pid_hash") && symbol_exists("pidhash_shift")) {
|
||||
if (VALID_MEMBER(pid_namespace_idr)) {
|
||||
STRUCT_SIZE_INIT(pid, "pid");
|
||||
tt->refresh_task_table = refresh_radix_tree_task_table;
|
||||
tt->pid_radix_tree = symbol_value("init_pid_ns") +
|
||||
OFFSET(pid_namespace_idr) + OFFSET(idr_idr_rt);
|
||||
tt->flags |= PID_RADIX_TREE;
|
||||
|
||||
} else if (symbol_exists("pid_hash") && symbol_exists("pidhash_shift")) {
|
||||
int pidhash_shift;
|
||||
|
||||
if (get_symbol_type("PIDTYPE_PID", NULL, &req) !=
|
||||
@ -2271,6 +2316,233 @@ chain_next:
|
||||
tt->retries = MAX(tt->retries, retries);
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux 4.15: pid_hash[] replaced by IDR/radix_tree
|
||||
*/
|
||||
static int
|
||||
radix_tree_task_callback(ulong task)
|
||||
{
|
||||
ulong *tlp;
|
||||
|
||||
if (tt->callbacks < tt->max_tasks) {
|
||||
tlp = (ulong *)tt->task_local;
|
||||
tlp += tt->callbacks++;
|
||||
*tlp = task;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
refresh_radix_tree_task_table(void)
|
||||
{
|
||||
int i, cnt;
|
||||
ulong count, retries, next, curtask, curpid, upid_ns, pid_tasks_0, task;
|
||||
ulong *tlp;
|
||||
char *tp;
|
||||
struct radix_tree_pair rtp;
|
||||
struct task_context *tc;
|
||||
char *pidbuf;
|
||||
|
||||
if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */
|
||||
return;
|
||||
|
||||
if (DUMPFILE()) { /* impossible */
|
||||
please_wait("gathering task table data");
|
||||
if (!symbol_exists("panic_threads"))
|
||||
tt->flags |= POPULATE_PANIC;
|
||||
}
|
||||
|
||||
if (ACTIVE() && !(tt->flags & TASK_REFRESH))
|
||||
return;
|
||||
|
||||
curpid = NO_PID;
|
||||
curtask = NO_TASK;
|
||||
|
||||
/*
|
||||
* The current task's task_context entry may change,
|
||||
* or the task may not even exist anymore.
|
||||
*/
|
||||
if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) {
|
||||
curtask = CURRENT_TASK();
|
||||
curpid = CURRENT_PID();
|
||||
}
|
||||
|
||||
count = do_radix_tree(tt->pid_radix_tree, RADIX_TREE_COUNT, NULL);
|
||||
if (CRASHDEBUG(1))
|
||||
console("do_radix_tree: count: %ld\n", count);
|
||||
|
||||
retries = 0;
|
||||
pidbuf = GETBUF(SIZE(pid));
|
||||
|
||||
retry_radix_tree:
|
||||
if (retries && DUMPFILE())
|
||||
error(FATAL,
|
||||
"\ncannot gather a stable task list via radix tree\n");
|
||||
|
||||
if ((retries == MAX_UNLIMITED_TASK_RETRIES) &&
|
||||
!(tt->flags & TASK_INIT_DONE))
|
||||
error(FATAL,
|
||||
"\ncannot gather a stable task list via radix tree (%d retries)\n",
|
||||
retries);
|
||||
|
||||
if (count > tt->max_tasks) {
|
||||
tt->max_tasks = count + TASK_SLUSH;
|
||||
allocate_task_space(tt->max_tasks);
|
||||
}
|
||||
|
||||
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
|
||||
tt->callbacks = 0;
|
||||
rtp.index = 0;
|
||||
rtp.value = (void *)&radix_tree_task_callback;
|
||||
count = do_radix_tree(tt->pid_radix_tree, RADIX_TREE_DUMP_CB, &rtp);
|
||||
if (CRASHDEBUG(1))
|
||||
console("do_radix_tree: count: %ld tt->callbacks: %d\n", count, tt->callbacks);
|
||||
|
||||
if (count > tt->max_tasks) {
|
||||
retries++;
|
||||
goto retry_radix_tree;
|
||||
}
|
||||
|
||||
if (!hq_open()) {
|
||||
error(INFO, "cannot hash task_struct entries\n");
|
||||
if (!(tt->flags & TASK_INIT_DONE))
|
||||
clean_exit(1);
|
||||
error(INFO, "using stale task_structs\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the idle threads first.
|
||||
*/
|
||||
cnt = 0;
|
||||
for (i = 0; i < kt->cpus; i++) {
|
||||
if (!tt->idle_threads[i])
|
||||
continue;
|
||||
if (hq_enter(tt->idle_threads[i]))
|
||||
cnt++;
|
||||
else
|
||||
error(WARNING, "%sduplicate idle tasks?\n",
|
||||
DUMPFILE() ? "\n" : "");
|
||||
}
|
||||
|
||||
for (i = 0; i < tt->max_tasks; i++) {
|
||||
tlp = (ulong *)tt->task_local;
|
||||
tlp += i;
|
||||
if ((next = *tlp) == 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Translate radix tree contents to PIDTYPE_PID task.
|
||||
* - the radix tree contents are struct pid pointers
|
||||
* - upid is contained in pid.numbers[0]
|
||||
* - upid.ns should point to init->init_pid_ns
|
||||
* - pid->tasks[0] is first hlist_node in task->pids[3]
|
||||
* - get task from address of task->pids[0]
|
||||
*/
|
||||
if (!readmem(next, KVADDR, pidbuf,
|
||||
SIZE(pid), "pid", RETURN_ON_ERROR|QUIET)) {
|
||||
error(INFO, "\ncannot read pid struct from radix tree\n");
|
||||
if (DUMPFILE())
|
||||
continue;
|
||||
hq_close();
|
||||
retries++;
|
||||
goto retry_radix_tree;
|
||||
}
|
||||
|
||||
upid_ns = ULONG(pidbuf + OFFSET(pid_numbers) + OFFSET(upid_ns));
|
||||
if (upid_ns != tt->init_pid_ns)
|
||||
continue;
|
||||
pid_tasks_0 = ULONG(pidbuf + OFFSET(pid_tasks));
|
||||
if (!pid_tasks_0)
|
||||
continue;
|
||||
task = pid_tasks_0 - OFFSET(task_struct_pids);
|
||||
|
||||
if (CRASHDEBUG(1))
|
||||
console("pid: %lx ns: %lx tasks[0]: %lx task: %lx\n",
|
||||
next, upid_ns, pid_tasks_0, task);
|
||||
|
||||
if (is_idle_thread(task))
|
||||
continue;
|
||||
|
||||
if (!IS_TASK_ADDR(task)) {
|
||||
error(INFO, "%s: IDR radix tree: invalid task address: %lx\n",
|
||||
DUMPFILE() ? "\n" : "", task);
|
||||
if (DUMPFILE())
|
||||
break;
|
||||
hq_close();
|
||||
retries++;
|
||||
goto retry_radix_tree;
|
||||
}
|
||||
|
||||
if (!hq_enter(task)) {
|
||||
error(INFO, "%s: IDR radix tree: duplicate task: %lx\n",
|
||||
DUMPFILE() ? "\n" : "", task);
|
||||
if (DUMPFILE())
|
||||
break;
|
||||
hq_close();
|
||||
retries++;
|
||||
goto retry_radix_tree;
|
||||
}
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
BZERO(tt->task_local, tt->max_tasks * sizeof(void *));
|
||||
cnt = retrieve_list((ulong *)tt->task_local, cnt);
|
||||
hq_close();
|
||||
|
||||
clear_task_cache();
|
||||
|
||||
for (i = 0, tlp = (ulong *)tt->task_local,
|
||||
tt->running_tasks = 0, tc = tt->context_array;
|
||||
i < tt->max_tasks; i++, tlp++) {
|
||||
if (!(*tlp))
|
||||
continue;
|
||||
|
||||
if (!IS_TASK_ADDR(*tlp)) {
|
||||
error(WARNING,
|
||||
"%sinvalid task address found in task list: %lx\n",
|
||||
DUMPFILE() ? "\n" : "", *tlp);
|
||||
if (DUMPFILE())
|
||||
continue;
|
||||
retries++;
|
||||
goto retry_radix_tree;
|
||||
}
|
||||
|
||||
if (task_exists(*tlp)) {
|
||||
error(WARNING,
|
||||
"%sduplicate task address found in task list: %lx\n",
|
||||
DUMPFILE() ? "\n" : "", *tlp);
|
||||
if (DUMPFILE())
|
||||
continue;
|
||||
retries++;
|
||||
goto retry_radix_tree;
|
||||
}
|
||||
|
||||
if (!(tp = fill_task_struct(*tlp))) {
|
||||
if (DUMPFILE())
|
||||
continue;
|
||||
retries++;
|
||||
goto retry_radix_tree;
|
||||
}
|
||||
|
||||
if (store_context(tc, *tlp, tp)) {
|
||||
tc++;
|
||||
tt->running_tasks++;
|
||||
}
|
||||
}
|
||||
|
||||
FREEBUF(pidbuf);
|
||||
|
||||
please_wait_done();
|
||||
|
||||
if (ACTIVE() && (tt->flags & TASK_INIT_DONE))
|
||||
refresh_context(curtask, curpid);
|
||||
|
||||
tt->retries = MAX(tt->retries, retries);
|
||||
}
|
||||
|
||||
static void
|
||||
refresh_active_task_table(void)
|
||||
{
|
||||
@ -7054,6 +7326,8 @@ dump_task_table(int verbose)
|
||||
fprintf(fp, "refresh_hlist_task_table_v3()\n");
|
||||
else if (tt->refresh_task_table == refresh_active_task_table)
|
||||
fprintf(fp, "refresh_active_task_table()\n");
|
||||
else if (tt->refresh_task_table == refresh_radix_tree_task_table)
|
||||
fprintf(fp, "refresh_radix_tree_task_table()\n");
|
||||
else
|
||||
fprintf(fp, "%lx\n", (ulong)tt->refresh_task_table);
|
||||
|
||||
@ -7090,6 +7364,9 @@ dump_task_table(int verbose)
|
||||
if (tt->flags & PID_HASH)
|
||||
sprintf(&buf[strlen(buf)],
|
||||
"%sPID_HASH", others++ ? "|" : "");
|
||||
if (tt->flags & PID_RADIX_TREE)
|
||||
sprintf(&buf[strlen(buf)],
|
||||
"%sPID_RADIX_TREE", others++ ? "|" : "");
|
||||
if (tt->flags & THREAD_INFO)
|
||||
sprintf(&buf[strlen(buf)],
|
||||
"%sTHREAD_INFO", others++ ? "|" : "");
|
||||
@ -7122,6 +7399,8 @@ dump_task_table(int verbose)
|
||||
fprintf(fp, " task_end: %lx\n", tt->task_end);
|
||||
fprintf(fp, " task_local: %lx\n", (ulong)tt->task_local);
|
||||
fprintf(fp, " max_tasks: %d\n", tt->max_tasks);
|
||||
fprintf(fp, " pid_radix_tree: %lx\n", tt->pid_radix_tree);
|
||||
fprintf(fp, " callbacks: %d\n", tt->callbacks);
|
||||
fprintf(fp, " nr_threads: %d\n", tt->nr_threads);
|
||||
fprintf(fp, " running_tasks: %ld\n", tt->running_tasks);
|
||||
fprintf(fp, " retries: %ld\n", tt->retries);
|
||||
|
Loading…
Reference in New Issue
Block a user