mirror of https://github.com/crash-utility/crash
1086 lines
30 KiB
C
1086 lines
30 KiB
C
/* ipcs.c - provide information on ipc facilities
|
|
*
|
|
* Copyright (C) 2012 FUJITSU LIMITED
|
|
* Auther: Qiao Nuohan <qiaonuohan@cn.fujitsu.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" /* From the crash source top-level directory */
|
|
|
|
#define SPECIFIED_NOTHING 0x0
|
|
#define SPECIFIED_ID 0x1
|
|
#define SPECIFIED_ADDR 0x2
|
|
|
|
#define IPCS_INIT 0x1
|
|
#define MAX_ID_SHIFT (sizeof(int)*8 - 1)
|
|
#define MAX_ID_BIT (1U << MAX_ID_SHIFT)
|
|
#define MAX_ID_MASK (MAX_ID_BIT - 1)
|
|
|
|
#define SHM_DEST 01000
|
|
#define SHM_LOCKED 02000
|
|
|
|
struct shm_info {
|
|
ulong shmid_kernel;
|
|
int key;
|
|
int shmid;
|
|
ulong rss;
|
|
ulong swap;
|
|
unsigned int uid;
|
|
unsigned int perms;
|
|
ulong bytes;
|
|
ulong nattch;
|
|
ulong shm_inode;
|
|
int deleted;
|
|
};
|
|
|
|
struct sem_info {
|
|
ulong sem_array;
|
|
int key;
|
|
int semid;
|
|
unsigned int uid;
|
|
unsigned int perms;
|
|
ulong nsems;
|
|
int deleted;
|
|
};
|
|
|
|
struct msg_info {
|
|
ulong msg_queue;
|
|
int key;
|
|
int msgid;
|
|
unsigned int uid;
|
|
unsigned int perms;
|
|
ulong bytes;
|
|
ulong messages;
|
|
int deleted;
|
|
};
|
|
|
|
struct ipcs_table {
|
|
int idr_bits;
|
|
ulong init_flags;
|
|
ulong hugetlbfs_f_op_addr;
|
|
ulong shm_f_op_addr;
|
|
ulong shm_f_op_huge_addr;
|
|
int use_shm_f_op;
|
|
int seq_multiplier;
|
|
};
|
|
|
|
/*
|
|
* function declaration
|
|
*/
|
|
|
|
static void ipcs_init(void);
|
|
static int dump_shared_memory(int, ulong, int, ulong);
|
|
static int dump_semaphore_arrays(int, ulong, int, ulong);
|
|
static int dump_message_queues(int, ulong, int, ulong);
|
|
static int ipc_search_idr(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int);
|
|
static int ipc_search_array(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int);
|
|
static ulong idr_find(ulong, int);
|
|
static int dump_shm_info(ulong, int, ulong, int, int);
|
|
static int dump_sem_info(ulong, int, ulong, int, int);
|
|
static int dump_msg_info(ulong, int, ulong, int, int);
|
|
static void get_shm_info(struct shm_info *, ulong, int);
|
|
static void get_sem_info(struct sem_info *, ulong, int);
|
|
static void get_msg_info(struct msg_info *, ulong, int);
|
|
static void add_rss_swap(ulong, int, ulong *, ulong *);
|
|
static int is_file_hugepages(ulong);
|
|
|
|
/*
|
|
* global data
|
|
*/
|
|
static struct ipcs_table ipcs_table = { 0 };
|
|
|
|
static void
|
|
ipcs_init(void)
|
|
{
|
|
if (ipcs_table.init_flags & IPCS_INIT) {
|
|
return;
|
|
}
|
|
|
|
ipcs_table.init_flags |= IPCS_INIT;
|
|
|
|
MEMBER_OFFSET_INIT(file_f_op, "file", "f_op");
|
|
MEMBER_OFFSET_INIT(file_private_data, "file", "private_data");
|
|
MEMBER_OFFSET_INIT(hstate_order, "hstate", "order");
|
|
MEMBER_OFFSET_INIT(hugetlbfs_sb_info_hstate, "hugetlbfs_sb_info",
|
|
"hstate");
|
|
MEMBER_OFFSET_INIT(idr_layers, "idr", "layers");
|
|
MEMBER_OFFSET_INIT(idr_layer_layer, "idr_layer", "layer");
|
|
MEMBER_OFFSET_INIT(idr_layer_ary, "idr_layer", "ary");
|
|
MEMBER_OFFSET_INIT(idr_top, "idr", "top");
|
|
MEMBER_OFFSET_INIT(ipc_id_ary_p, "ipc_id_ary", "p");
|
|
MEMBER_OFFSET_INIT(ipc_ids_entries, "ipc_ids", "entries");
|
|
MEMBER_OFFSET_INIT(ipc_ids_max_id, "ipc_ids", "max_id");
|
|
MEMBER_OFFSET_INIT(ipc_ids_in_use, "ipc_ids", "in_use");
|
|
MEMBER_OFFSET_INIT(ipc_ids_ipcs_idr, "ipc_ids", "ipcs_idr");
|
|
MEMBER_OFFSET_INIT(ipc_namespace_ids, "ipc_namespace", "ids");
|
|
MEMBER_OFFSET_INIT(kern_ipc_perm_key, "kern_ipc_perm", "key");
|
|
MEMBER_OFFSET_INIT(kern_ipc_perm_id, "kern_ipc_perm", "id");
|
|
MEMBER_OFFSET_INIT(kern_ipc_perm_uid, "kern_ipc_perm", "uid");
|
|
MEMBER_OFFSET_INIT(kern_ipc_perm_mode, "kern_ipc_perm", "mode");
|
|
MEMBER_OFFSET_INIT(kern_ipc_perm_deleted, "kern_ipc_perm",
|
|
"deleted");
|
|
MEMBER_OFFSET_INIT(kern_ipc_perm_seq, "kern_ipc_perm", "seq");
|
|
MEMBER_OFFSET_INIT(nsproxy_ipc_ns, "nsproxy", "ipc_ns");
|
|
MEMBER_OFFSET_INIT(shmem_inode_info_vfs_inode, "shmem_inode_info",
|
|
"vfs_inode");
|
|
MEMBER_OFFSET_INIT(shmem_inode_info_swapped, "shmem_inode_info",
|
|
"swapped");
|
|
if (INVALID_MEMBER(shmem_inode_info_swapped))
|
|
ANON_MEMBER_OFFSET_INIT(shmem_inode_info_swapped,
|
|
"shmem_inode_info", "swapped");
|
|
MEMBER_OFFSET_INIT(shm_file_data_file, "shm_file_data", "file");
|
|
MEMBER_OFFSET_INIT(shmid_kernel_shm_perm, "shmid_kernel",
|
|
"shm_perm");
|
|
MEMBER_OFFSET_INIT(shmid_kernel_shm_segsz, "shmid_kernel",
|
|
"shm_segsz");
|
|
MEMBER_OFFSET_INIT(shmid_kernel_shm_nattch, "shmid_kernel",
|
|
"shm_nattch");
|
|
MEMBER_OFFSET_INIT(shmid_kernel_shm_file, "shmid_kernel",
|
|
"shm_file");
|
|
MEMBER_OFFSET_INIT(shmid_kernel_id, "shmid_kernel", "id");
|
|
MEMBER_OFFSET_INIT(sem_array_sem_perm, "sem_array", "sem_perm");
|
|
MEMBER_OFFSET_INIT(sem_array_sem_id, "sem_array", "sem_id");
|
|
MEMBER_OFFSET_INIT(sem_array_sem_nsems, "sem_array", "sem_nsems");
|
|
MEMBER_OFFSET_INIT(msg_queue_q_perm, "msg_queue", "q_perm");
|
|
MEMBER_OFFSET_INIT(msg_queue_q_id, "msg_queue", "q_id");
|
|
MEMBER_OFFSET_INIT(msg_queue_q_cbytes, "msg_queue", "q_cbytes");
|
|
MEMBER_OFFSET_INIT(msg_queue_q_qnum, "msg_queue", "q_qnum");
|
|
MEMBER_OFFSET_INIT(super_block_s_fs_info, "super_block",
|
|
"s_fs_info");
|
|
|
|
/*
|
|
* struct size
|
|
*/
|
|
STRUCT_SIZE_INIT(ipc_ids, "ipc_ids");
|
|
STRUCT_SIZE_INIT(shmid_kernel, "shmid_kernel");
|
|
STRUCT_SIZE_INIT(sem_array, "sem_array");
|
|
STRUCT_SIZE_INIT(msg_queue, "msg_queue");
|
|
STRUCT_SIZE_INIT(hstate, "hstate");
|
|
|
|
if (symbol_exists("hugetlbfs_file_operations"))
|
|
ipcs_table.hugetlbfs_f_op_addr =
|
|
symbol_value("hugetlbfs_file_operations");
|
|
if (symbol_exists("is_file_shm_hugepages")) {
|
|
ipcs_table.use_shm_f_op = TRUE;
|
|
ipcs_table.shm_f_op_addr =
|
|
symbol_value("shm_file_operations");
|
|
if (symbol_exists("shm_file_operations_huge")) {
|
|
ipcs_table.shm_f_op_huge_addr =
|
|
symbol_value("shm_file_operations_huge");
|
|
} else {
|
|
ipcs_table.shm_f_op_huge_addr = -1;
|
|
}
|
|
} else {
|
|
ipcs_table.use_shm_f_op = FALSE;
|
|
ipcs_table.shm_f_op_addr = -1;
|
|
ipcs_table.shm_f_op_huge_addr = -1;
|
|
}
|
|
|
|
if (BITS32())
|
|
ipcs_table.idr_bits = 5;
|
|
else if (BITS64())
|
|
ipcs_table.idr_bits = 6;
|
|
else
|
|
error(FATAL, "machdep->bits is not 32 or 64");
|
|
|
|
ipcs_table.seq_multiplier = 32768;
|
|
}
|
|
|
|
/*
|
|
* Arguments are passed to the command functions in the global args[argcnt]
|
|
* array. See getopt(3) for info on dash arguments. Check out defs.h and
|
|
* other crash commands for usage of the myriad of utility routines available
|
|
* to accomplish what your task.
|
|
*/
|
|
void
|
|
cmd_ipcs(void)
|
|
{
|
|
int specified;
|
|
char *specified_value[MAXARGS];
|
|
int value_index;
|
|
int c;
|
|
int shm, sem, msg, verbose;
|
|
int i;
|
|
ulong value, task;
|
|
int found;
|
|
struct task_context *tc;
|
|
char buf[BUFSIZE];
|
|
|
|
value_index = 0;
|
|
specified = SPECIFIED_NOTHING;
|
|
shm = 0;
|
|
sem = 0;
|
|
msg = 0;
|
|
verbose = 0;
|
|
tc = NULL;
|
|
|
|
while ((c = getopt(argcnt, args, "smMqn:")) != EOF) {
|
|
switch(c) {
|
|
case 's':
|
|
sem = 1;
|
|
break;
|
|
case 'm':
|
|
shm = 1;
|
|
break;
|
|
case 'M':
|
|
shm = 1;
|
|
verbose = 1;
|
|
break;
|
|
case 'q':
|
|
msg = 1;
|
|
break;
|
|
case 'n':
|
|
switch (str_to_context(optarg, &value, &tc)) {
|
|
case STR_PID:
|
|
case STR_TASK:
|
|
break;
|
|
case STR_INVALID:
|
|
error(FATAL, "invalid task or pid value: %s\n",
|
|
optarg);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
cmd_usage(pc->curcmd, SYNOPSIS);;
|
|
return;
|
|
}
|
|
}
|
|
|
|
while (args[optind]) {
|
|
if (value_index >= MAXARGS)
|
|
error(FATAL, "too many id/member specified\n");
|
|
specified |= SPECIFIED_ID | SPECIFIED_ADDR;
|
|
specified_value[value_index] = args[optind];
|
|
stol(args[optind], FAULT_ON_ERROR, NULL);
|
|
optind++;
|
|
value_index++;
|
|
}
|
|
|
|
if (THIS_KERNEL_VERSION < LINUX(2,6,0))
|
|
command_not_supported();
|
|
|
|
ipcs_init();
|
|
|
|
if (!shm && !sem && !msg)
|
|
shm = sem = msg = 1;
|
|
|
|
task = tc ? tc->task : pid_to_task(0);
|
|
|
|
if (!value_index) {
|
|
if (shm)
|
|
dump_shared_memory(specified, 0, verbose, task);
|
|
if (sem)
|
|
dump_semaphore_arrays(specified, 0, 0, task);
|
|
if (msg)
|
|
dump_message_queues(specified, 0, 0, task);
|
|
} else {
|
|
open_tmpfile();
|
|
i = 0;
|
|
while (i < value_index) {
|
|
found = 0;
|
|
value = stol(specified_value[i], FAULT_ON_ERROR, NULL);
|
|
if (shm)
|
|
found += dump_shared_memory(specified,
|
|
value, verbose, task);
|
|
if (sem)
|
|
found += dump_semaphore_arrays(specified,
|
|
value, 0, task);
|
|
if (msg)
|
|
found += dump_message_queues(specified,
|
|
value, 0, task);
|
|
|
|
if (!found)
|
|
fprintf(pc->saved_fp, "invalid id or address: %s\n\n",
|
|
specified_value[i]);
|
|
|
|
i++;
|
|
}
|
|
fflush(fp);
|
|
rewind(fp);
|
|
|
|
while (fgets(buf, BUFSIZE, fp))
|
|
fprintf(pc->saved_fp, "%s", buf);
|
|
|
|
close_tmpfile();
|
|
}
|
|
}
|
|
|
|
static int
|
|
dump_shared_memory(int specified, ulong specified_value, int verbose, ulong task)
|
|
{
|
|
ulong nsproxy_p, ipc_ns_p;
|
|
ulong ipc_ids_p;
|
|
int (*ipc_search)(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int);
|
|
int (*dump_shm)(ulong, int, ulong, int, int);
|
|
char buf0[BUFSIZE];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE];
|
|
char buf5[BUFSIZE];
|
|
char buf6[BUFSIZE];
|
|
char buf7[BUFSIZE];
|
|
|
|
if (!verbose && specified == SPECIFIED_NOTHING) {
|
|
fprintf(fp, "%s %s %s %s %s %s %s %s\n",
|
|
mkstring(buf0, VADDR_PRLEN<=12?12:VADDR_PRLEN,
|
|
LJUST, "SHMID_KERNEL"),
|
|
mkstring(buf1, 8, LJUST, "KEY"),
|
|
mkstring(buf2, 10, LJUST, "SHMID"),
|
|
mkstring(buf3, 5, LJUST, "UID"),
|
|
mkstring(buf4, 5, LJUST, "PERMS"),
|
|
mkstring(buf5, 10, LJUST, "BYTES"),
|
|
mkstring(buf6, 6, LJUST, "NATTCH"),
|
|
mkstring(buf7, 6, LJUST, "STATUS"));
|
|
}
|
|
|
|
dump_shm = dump_shm_info;
|
|
|
|
if (VALID_MEMBER(kern_ipc_perm_id)) {
|
|
ipc_search = ipc_search_idr;
|
|
} else {
|
|
ipc_search = ipc_search_array;
|
|
}
|
|
|
|
if (symbol_exists("shm_ids")) {
|
|
ipc_ids_p = symbol_value("shm_ids");
|
|
} else {
|
|
readmem(task + OFFSET(task_struct_nsproxy), KVADDR,
|
|
&nsproxy_p, sizeof(ulong), "task_struct.nsproxy",
|
|
FAULT_ON_ERROR);
|
|
if (!readmem(nsproxy_p + OFFSET(nsproxy_ipc_ns), KVADDR,
|
|
&ipc_ns_p, sizeof(ulong), "nsproxy.ipc_ns",
|
|
RETURN_ON_ERROR|QUIET))
|
|
error(FATAL,
|
|
"cannot determine ipc_namespace location!\n");
|
|
|
|
if (MEMBER_SIZE("ipc_namespace","ids") == sizeof(ulong) * 3)
|
|
readmem(ipc_ns_p + OFFSET(ipc_namespace_ids) +
|
|
sizeof(ulong) * 2, KVADDR, &ipc_ids_p,
|
|
sizeof(ulong), "ipc_namespace.ids[2]",
|
|
FAULT_ON_ERROR);
|
|
else
|
|
ipc_ids_p = ipc_ns_p + OFFSET(ipc_namespace_ids) +
|
|
2 * SIZE(ipc_ids);
|
|
}
|
|
|
|
if (ipc_search(ipc_ids_p, specified, specified_value, dump_shm, verbose)) {
|
|
return 1;
|
|
} else {
|
|
if (verbose && specified == SPECIFIED_NOTHING) {
|
|
fprintf(fp, "%s %s %s %s %s %s %s %s\n",
|
|
mkstring(buf0, VADDR_PRLEN<=12?12:VADDR_PRLEN,
|
|
LJUST, "SHMID_KERNEL"),
|
|
mkstring(buf1, 8, LJUST, "KEY"),
|
|
mkstring(buf2, 10, LJUST, "SHMID"),
|
|
mkstring(buf3, 5, LJUST, "UID"),
|
|
mkstring(buf4, 5, LJUST, "PERMS"),
|
|
mkstring(buf5, 10, LJUST, "BYTES"),
|
|
mkstring(buf6, 6, LJUST, "NATTCH"),
|
|
mkstring(buf7, 6, LJUST, "STATUS"));
|
|
fprintf(fp, "(none allocated)\n\n");
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
dump_semaphore_arrays(int specified, ulong specified_value, int verbose, ulong task)
|
|
{
|
|
ulong nsproxy_p, ipc_ns_p;
|
|
ulong ipc_ids_p;
|
|
int (*ipc_search)(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int);
|
|
int (*dump_sem)(ulong, int, ulong, int, int);
|
|
char buf0[BUFSIZE];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE];
|
|
char buf5[BUFSIZE];
|
|
|
|
if (specified == SPECIFIED_NOTHING) {
|
|
fprintf(fp, "%s %s %s %s %s %s\n",
|
|
mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN,
|
|
LJUST, "SEM_ARRAY"),
|
|
mkstring(buf1, 8, LJUST, "KEY"),
|
|
mkstring(buf2, 10, LJUST, "SEMID"),
|
|
mkstring(buf3, 5, LJUST, "UID"),
|
|
mkstring(buf4, 5, LJUST, "PERMS"),
|
|
mkstring(buf5, 10, LJUST, "NSEMS"));
|
|
}
|
|
|
|
dump_sem = dump_sem_info;
|
|
|
|
if (VALID_MEMBER(kern_ipc_perm_id)) {
|
|
ipc_search = ipc_search_idr;
|
|
} else {
|
|
ipc_search = ipc_search_array;
|
|
}
|
|
|
|
if (symbol_exists("sem_ids")) {
|
|
ipc_ids_p = symbol_value("sem_ids");
|
|
} else {
|
|
readmem(task + OFFSET(task_struct_nsproxy), KVADDR,
|
|
&nsproxy_p, sizeof(ulong), "task_struct.nsproxy",
|
|
FAULT_ON_ERROR);
|
|
|
|
if (!readmem(nsproxy_p + OFFSET(nsproxy_ipc_ns), KVADDR,
|
|
&ipc_ns_p, sizeof(ulong), "nsproxy.ipc_ns",
|
|
FAULT_ON_ERROR|QUIET))
|
|
error(FATAL,
|
|
"cannot determine ipc_namespace location!\n");
|
|
|
|
if (MEMBER_SIZE("ipc_namespace","ids") == sizeof(ulong) * 3)
|
|
readmem(ipc_ns_p + OFFSET(ipc_namespace_ids),
|
|
KVADDR, &ipc_ids_p, sizeof(ulong),
|
|
"ipc_namespace.ids[2]", FAULT_ON_ERROR);
|
|
else
|
|
ipc_ids_p = ipc_ns_p + OFFSET(ipc_namespace_ids);
|
|
}
|
|
|
|
return ipc_search(ipc_ids_p, specified, specified_value, dump_sem, verbose);
|
|
}
|
|
|
|
static int
|
|
dump_message_queues(int specified, ulong specified_value, int verbose, ulong task)
|
|
{
|
|
ulong nsproxy_p, ipc_ns_p;
|
|
ulong ipc_ids_p;
|
|
int (*ipc_search)(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int);
|
|
int (*dump_msg)(ulong, int, ulong, int, int);
|
|
char buf0[BUFSIZE];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE];
|
|
char buf5[BUFSIZE];
|
|
char buf6[BUFSIZE];
|
|
|
|
if (specified == SPECIFIED_NOTHING) {
|
|
fprintf(fp, "%s %s %s %s %s %s %s\n",
|
|
mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN,
|
|
LJUST, "MSG_QUEUE"),
|
|
mkstring(buf1, 8, LJUST, "KEY"),
|
|
mkstring(buf2, 10, LJUST, "MSQID"),
|
|
mkstring(buf3, 5, LJUST, "UID"),
|
|
mkstring(buf4, 5, LJUST, "PERMS"),
|
|
mkstring(buf5, 12, LJUST, "USED-BYTES"),
|
|
mkstring(buf6, 12, LJUST, "MESSAGES"));
|
|
}
|
|
|
|
dump_msg = dump_msg_info;
|
|
|
|
if (VALID_MEMBER(kern_ipc_perm_id)) {
|
|
ipc_search = ipc_search_idr;
|
|
} else {
|
|
ipc_search = ipc_search_array;
|
|
}
|
|
|
|
if (symbol_exists("msg_ids")) {
|
|
ipc_ids_p = symbol_value("msg_ids");
|
|
} else {
|
|
readmem(task + OFFSET(task_struct_nsproxy), KVADDR,
|
|
&nsproxy_p, sizeof(ulong), "task_struct.nsproxy",
|
|
FAULT_ON_ERROR);
|
|
if (!readmem(nsproxy_p + OFFSET(nsproxy_ipc_ns), KVADDR,
|
|
&ipc_ns_p, sizeof(ulong), "nsproxy.ipc_ns",
|
|
FAULT_ON_ERROR|QUIET))
|
|
error(FATAL,
|
|
"cannot determine ipc_namespace location!\n");
|
|
|
|
if (MEMBER_SIZE("ipc_namespace","ids") == sizeof(ulong) * 3)
|
|
readmem(ipc_ns_p + OFFSET(ipc_namespace_ids) +
|
|
sizeof(ulong), KVADDR, &ipc_ids_p,
|
|
sizeof(ulong), "ipc_namespace.ids[2]",
|
|
FAULT_ON_ERROR);
|
|
else
|
|
ipc_ids_p = ipc_ns_p + OFFSET(ipc_namespace_ids) +
|
|
SIZE(ipc_ids);
|
|
}
|
|
|
|
return ipc_search(ipc_ids_p, specified, specified_value, dump_msg, verbose);
|
|
}
|
|
|
|
/*
|
|
* if shared memory information is stored in an array, use this function.
|
|
*/
|
|
static int
|
|
ipc_search_array(ulong ipc_ids_p, int specified, ulong specified_value, int (*fn)(ulong, int, ulong, int, int), int verbose)
|
|
{
|
|
ulong entries_p;
|
|
int max_id, i;
|
|
ulong *array;
|
|
int found = 0;
|
|
int allocated = 0;
|
|
|
|
readmem(ipc_ids_p + OFFSET(ipc_ids_entries), KVADDR, &entries_p,
|
|
sizeof(ulong), "ipc_ids.entries", FAULT_ON_ERROR);
|
|
readmem(ipc_ids_p + OFFSET(ipc_ids_max_id), KVADDR, &max_id,
|
|
sizeof(int), "ipc_ids.max_id", FAULT_ON_ERROR);
|
|
|
|
if (max_id < 0) {
|
|
if (specified == SPECIFIED_NOTHING && !verbose)
|
|
fprintf(fp, "(none allocated)\n\n");
|
|
return 0;
|
|
}
|
|
|
|
array = (ulong *)GETBUF(sizeof(ulong *) * (max_id + 1));
|
|
if (VALID_MEMBER(ipc_id_ary_p))
|
|
readmem(entries_p + OFFSET(ipc_id_ary_p), KVADDR, array,
|
|
sizeof(ulong *) * (max_id + 1), "ipc_id_ary.p",
|
|
FAULT_ON_ERROR);
|
|
else
|
|
readmem(entries_p, KVADDR, array, sizeof(ulong *)*(max_id+1),
|
|
"ipc_id array", FAULT_ON_ERROR);
|
|
|
|
for (i=0; i<=max_id; i++) {
|
|
if (array[i] == 0)
|
|
continue;
|
|
if (fn(array[i], specified, specified_value, i, verbose)) {
|
|
allocated++;
|
|
found = 1;
|
|
if (specified != SPECIFIED_NOTHING)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (specified == SPECIFIED_NOTHING && !verbose) {
|
|
if (!allocated)
|
|
fprintf(fp, "(none allocated)\n");
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
FREEBUF(array);
|
|
|
|
if (found)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* if shared memory information is stored by using idr, use this function to
|
|
* get data.
|
|
*/
|
|
static int
|
|
ipc_search_idr(ulong ipc_ids_p, int specified, ulong specified_value, int (*fn)(ulong, int, ulong, int, int), int verbose)
|
|
{
|
|
int in_use;
|
|
ulong ipcs_idr_p;
|
|
ulong ipc;
|
|
int next_id, total;
|
|
int found = 0;
|
|
|
|
readmem(ipc_ids_p + OFFSET(ipc_ids_in_use), KVADDR, &in_use,
|
|
sizeof(int), "ipc_ids.in_use", FAULT_ON_ERROR);
|
|
|
|
ipcs_idr_p = ipc_ids_p + OFFSET(ipc_ids_ipcs_idr);
|
|
|
|
if (!in_use) {
|
|
if (specified == SPECIFIED_NOTHING && !verbose)
|
|
fprintf(fp, "(none allocated)\n\n");
|
|
return 0;
|
|
}
|
|
|
|
for (total = 0, next_id = 0; total < in_use; next_id++) {
|
|
ipc = idr_find(ipcs_idr_p, next_id);
|
|
if (ipc == 0)
|
|
continue;
|
|
|
|
total++;
|
|
if (fn(ipc, specified, specified_value, next_id, verbose)) {
|
|
found = 1;
|
|
if (specified != SPECIFIED_NOTHING)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!verbose && specified == SPECIFIED_NOTHING)
|
|
fprintf(fp, "\n");
|
|
|
|
if (found || specified == SPECIFIED_NOTHING)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* search every idr_layer
|
|
*/
|
|
static ulong
|
|
idr_find(ulong idp, int id)
|
|
{
|
|
ulong idr_layer_p;
|
|
int layer;
|
|
int idr_layers;
|
|
int n;
|
|
int index;
|
|
|
|
readmem(idp + OFFSET(idr_top), KVADDR, &idr_layer_p,
|
|
sizeof(ulong), "idr.top", FAULT_ON_ERROR);
|
|
|
|
if (!idr_layer_p)
|
|
return 0;
|
|
|
|
if (VALID_MEMBER(idr_layer_layer)) {
|
|
readmem(idr_layer_p + OFFSET(idr_layer_layer), KVADDR,
|
|
&layer, sizeof(int), "idr_layer.layer",
|
|
FAULT_ON_ERROR);
|
|
n = (layer + 1) * ipcs_table.idr_bits;
|
|
} else {
|
|
readmem(idp + OFFSET(idr_layers), KVADDR, &idr_layers,
|
|
sizeof(int), "idr.layers", FAULT_ON_ERROR);
|
|
n = idr_layers * ipcs_table.idr_bits;
|
|
}
|
|
id &= MAX_ID_MASK;
|
|
|
|
if (id >= (1 << n))
|
|
return 0;
|
|
|
|
while (n > 0 && idr_layer_p) {
|
|
n -= ipcs_table.idr_bits;
|
|
index = (id >> n) & ((1 << ipcs_table.idr_bits) - 1);
|
|
readmem(idr_layer_p + OFFSET(idr_layer_ary) +
|
|
sizeof(ulong) * index, KVADDR, &idr_layer_p,
|
|
sizeof(ulong), "idr_layer.ary", FAULT_ON_ERROR);
|
|
}
|
|
|
|
return idr_layer_p;
|
|
}
|
|
|
|
/*
|
|
* only specified is not SPECIFIED_NOTHIND, and the specified_value is found,
|
|
* then return 1
|
|
*/
|
|
static int
|
|
dump_shm_info(ulong shp, int specified, ulong specified_value, int id, int verbose)
|
|
{
|
|
struct shm_info shm_info;
|
|
char buf[BUFSIZE];
|
|
char buf0[BUFSIZE];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE];
|
|
char buf5[BUFSIZE];
|
|
char buf6[BUFSIZE];
|
|
char buf7[BUFSIZE];
|
|
|
|
get_shm_info(&shm_info, shp, id);
|
|
|
|
if (shm_info.deleted)
|
|
return 0;
|
|
|
|
if (((specified & SPECIFIED_ID) && shm_info.shmid == specified_value) ||
|
|
((specified & SPECIFIED_ADDR) && shm_info.shmid_kernel ==
|
|
specified_value) || specified == SPECIFIED_NOTHING) {
|
|
if (verbose || specified != SPECIFIED_NOTHING) {
|
|
fprintf(fp, "%s %s %s %s %s %s %s %s\n",
|
|
mkstring(buf0, VADDR_PRLEN<=12?12:VADDR_PRLEN,
|
|
LJUST, "SHMID_KERNEL"),
|
|
mkstring(buf1, 8, LJUST, "KEY"),
|
|
mkstring(buf2, 10, LJUST, "SHMID"),
|
|
mkstring(buf3, 5, LJUST, "UID"),
|
|
mkstring(buf4, 5, LJUST, "PERMS"),
|
|
mkstring(buf5, 10, LJUST, "BYTES"),
|
|
mkstring(buf6, 6, LJUST, "NATTCH"),
|
|
mkstring(buf7, 6, LJUST, "STATUS"));
|
|
}
|
|
|
|
fprintf(fp, "%s %08x %-10d %-5d %-5o %-10ld %-6ld %-s %-s\n",
|
|
mkstring(buf, VADDR_PRLEN <= 12 ? 12 : VADDR_PRLEN,
|
|
LJUST|LONG_HEX, (char *)shm_info.shmid_kernel),
|
|
shm_info.key,
|
|
shm_info.shmid,
|
|
shm_info.uid,
|
|
shm_info.perms & 0777,
|
|
shm_info.bytes,
|
|
shm_info.nattch,
|
|
shm_info.perms & SHM_DEST ? "dest" : "",
|
|
shm_info.perms & SHM_LOCKED ? "locked" : "");
|
|
|
|
if (verbose) {
|
|
fprintf(fp, "PAGES ALLOCATED/RESIDENT/SWAPPED: %ld/%ld/%ld\n",
|
|
(shm_info.bytes+PAGESIZE()-1) >> PAGESHIFT(),
|
|
shm_info.rss, shm_info.swap);
|
|
fprintf(fp, "INODE: %lx\n", shm_info.shm_inode);
|
|
}
|
|
|
|
if (verbose || specified != SPECIFIED_NOTHING)
|
|
fprintf(fp, "\n");
|
|
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* only specified is not SPECIFIED_NOTHIND, and the specified_value is found,
|
|
* then return 1
|
|
*/
|
|
static int
|
|
dump_sem_info(ulong shp, int specified, ulong specified_value, int id, int verbose)
|
|
{
|
|
struct sem_info sem_info;
|
|
char buf[BUFSIZE];
|
|
char buf0[BUFSIZE];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE];
|
|
char buf5[BUFSIZE];
|
|
|
|
get_sem_info(&sem_info, shp, id);
|
|
|
|
if (sem_info.deleted)
|
|
return 0;
|
|
|
|
if (((specified & SPECIFIED_ID) && sem_info.semid == specified_value) ||
|
|
((specified & SPECIFIED_ADDR) && sem_info.sem_array ==
|
|
specified_value) || specified == SPECIFIED_NOTHING) {
|
|
if (specified != SPECIFIED_NOTHING) {
|
|
fprintf(fp, "%s %s %s %s %s %s\n",
|
|
mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN,
|
|
LJUST, "SEM_ARRAY"),
|
|
mkstring(buf1, 8, LJUST, "KEY"),
|
|
mkstring(buf2, 10, LJUST, "SEMID"),
|
|
mkstring(buf3, 5, LJUST, "UID"),
|
|
mkstring(buf4, 5, LJUST, "PERMS"),
|
|
mkstring(buf5, 10, LJUST, "NSEMS"));
|
|
}
|
|
|
|
fprintf(fp, "%s %08x %-10d %-5d %-5o %-10ld\n",
|
|
mkstring(buf, VADDR_PRLEN <= 10 ? 10 : VADDR_PRLEN,
|
|
LJUST|LONG_HEX, (char *)sem_info.sem_array),
|
|
sem_info.key,
|
|
sem_info.semid,
|
|
sem_info.uid,
|
|
sem_info.perms & 0777,
|
|
sem_info.nsems);
|
|
|
|
if (specified != SPECIFIED_NOTHING)
|
|
fprintf(fp, "\n");
|
|
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* only specified is not SPECIFIED_NOTHIND, and the specified_value is found,
|
|
* then return 1
|
|
*/
|
|
static int
|
|
dump_msg_info(ulong shp, int specified, ulong specified_value, int id, int verbose)
|
|
{
|
|
struct msg_info msg_info;
|
|
char buf[BUFSIZE];
|
|
char buf0[BUFSIZE];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE];
|
|
char buf5[BUFSIZE];
|
|
char buf6[BUFSIZE];
|
|
|
|
get_msg_info(&msg_info, shp, id);
|
|
|
|
if (msg_info.deleted)
|
|
return 0;
|
|
|
|
if (((specified & SPECIFIED_ID) && msg_info.msgid == specified_value) ||
|
|
((specified & SPECIFIED_ADDR) && msg_info.msg_queue ==
|
|
specified_value) || specified == SPECIFIED_NOTHING) {
|
|
if (specified != SPECIFIED_NOTHING) {
|
|
fprintf(fp, "%s %s %s %s %s %s %s\n",
|
|
mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN,
|
|
LJUST, "MSG_QUEUE"),
|
|
mkstring(buf1, 8, LJUST, "KEY"),
|
|
mkstring(buf2, 10, LJUST, "MSQID"),
|
|
mkstring(buf3, 5, LJUST, "UID"),
|
|
mkstring(buf4, 5, LJUST, "PERMS"),
|
|
mkstring(buf5, 12, LJUST, "USED-BYTES"),
|
|
mkstring(buf6, 12, LJUST, "MESSAGES"));
|
|
}
|
|
|
|
fprintf(fp, "%s %08x %-10d %-5d %-5o %-12ld %-12ld\n",
|
|
mkstring(buf, VADDR_PRLEN <= 10 ? 10 : VADDR_PRLEN,
|
|
LJUST|LONG_HEX, (char *)msg_info.msg_queue),
|
|
msg_info.key,
|
|
msg_info.msgid,
|
|
msg_info.uid,
|
|
msg_info.perms & 0777,
|
|
msg_info.bytes,
|
|
msg_info.messages);
|
|
|
|
if (specified != SPECIFIED_NOTHING)
|
|
fprintf(fp, "\n");
|
|
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
get_shm_info(struct shm_info *shm_info, ulong shp, int id)
|
|
{
|
|
char buf[BUFSIZE];
|
|
ulong filep, dentryp, inodep;
|
|
|
|
shm_info->shmid_kernel = shp - OFFSET(shmid_kernel_shm_perm);
|
|
|
|
/*
|
|
* cache shmid_kernel
|
|
*/
|
|
readmem(shm_info->shmid_kernel, KVADDR, buf, SIZE(shmid_kernel),
|
|
"shmid_kernel", FAULT_ON_ERROR);
|
|
|
|
shm_info->key = INT(buf + OFFSET(shmid_kernel_shm_perm) +
|
|
OFFSET(kern_ipc_perm_key));
|
|
if (VALID_MEMBER(shmid_kernel_id))
|
|
shm_info->shmid = INT(buf + OFFSET(shmid_kernel_id));
|
|
else
|
|
shm_info->shmid = INT(buf +
|
|
OFFSET(shmid_kernel_shm_perm) +
|
|
OFFSET(kern_ipc_perm_id));
|
|
|
|
shm_info->uid = UINT(buf + OFFSET(shmid_kernel_shm_perm) +
|
|
OFFSET(kern_ipc_perm_uid));
|
|
|
|
if (BITS32())
|
|
shm_info->perms = USHORT(buf +
|
|
OFFSET(shmid_kernel_shm_perm) +
|
|
OFFSET(kern_ipc_perm_mode));
|
|
else
|
|
shm_info->perms = UINT(buf +
|
|
OFFSET(shmid_kernel_shm_perm) +
|
|
OFFSET(kern_ipc_perm_mode));
|
|
|
|
shm_info->bytes = ULONG(buf + OFFSET(shmid_kernel_shm_segsz));
|
|
|
|
shm_info->nattch = ULONG(buf + OFFSET(shmid_kernel_shm_nattch));
|
|
|
|
filep = ULONG(buf + OFFSET(shmid_kernel_shm_file));
|
|
readmem(filep + OFFSET(file_f_dentry), KVADDR, &dentryp, sizeof(ulong),
|
|
"file.f_dentry", FAULT_ON_ERROR);
|
|
readmem(dentryp + OFFSET(dentry_d_inode), KVADDR, &inodep,
|
|
sizeof(ulong), "dentry.d_inode", FAULT_ON_ERROR);
|
|
/*
|
|
* shm_inode here is the vfs_inode of struct shmem_inode_info
|
|
*/
|
|
shm_info->shm_inode = inodep;
|
|
|
|
shm_info->rss = 0;
|
|
shm_info->swap = 0;
|
|
|
|
add_rss_swap(inodep, is_file_hugepages(filep), &shm_info->rss,
|
|
&shm_info->swap);
|
|
|
|
shm_info->deleted = UINT(buf + OFFSET(shmid_kernel_shm_perm) +
|
|
OFFSET(kern_ipc_perm_deleted));
|
|
}
|
|
|
|
static void
|
|
get_sem_info(struct sem_info *sem_info, ulong shp, int id)
|
|
{
|
|
char buf[BUFSIZE];
|
|
|
|
sem_info->sem_array = shp - OFFSET(sem_array_sem_perm);
|
|
|
|
/*
|
|
* cache sem_array
|
|
*/
|
|
readmem(sem_info->sem_array, KVADDR, buf, SIZE(sem_array),
|
|
"sem_array", FAULT_ON_ERROR);
|
|
|
|
sem_info->key = INT(buf + OFFSET(sem_array_sem_perm) +
|
|
OFFSET(kern_ipc_perm_key));
|
|
|
|
if (VALID_MEMBER(sem_array_sem_id))
|
|
sem_info->semid = INT(buf + OFFSET(sem_array_sem_id));
|
|
else if (VALID_MEMBER(kern_ipc_perm_id))
|
|
sem_info->semid = INT(buf + OFFSET(sem_array_sem_perm) +
|
|
OFFSET(kern_ipc_perm_id));
|
|
else {
|
|
ulong seq;
|
|
seq = ULONG(buf + OFFSET(sem_array_sem_perm) +
|
|
OFFSET(kern_ipc_perm_seq));
|
|
sem_info->semid = ipcs_table.seq_multiplier * seq + id;
|
|
}
|
|
|
|
sem_info->uid = UINT(buf + OFFSET(sem_array_sem_perm) +
|
|
OFFSET(kern_ipc_perm_uid));
|
|
|
|
if (BITS32())
|
|
sem_info->perms = USHORT(buf +
|
|
OFFSET(sem_array_sem_perm) +
|
|
OFFSET(kern_ipc_perm_mode));
|
|
else
|
|
sem_info->perms = UINT(buf + OFFSET(sem_array_sem_perm) +
|
|
OFFSET(kern_ipc_perm_mode));
|
|
|
|
sem_info->nsems = ULONG(buf + OFFSET(sem_array_sem_nsems));
|
|
|
|
sem_info->deleted = UINT(buf + OFFSET(sem_array_sem_perm) +
|
|
OFFSET(kern_ipc_perm_deleted));
|
|
}
|
|
|
|
static void
|
|
get_msg_info(struct msg_info *msg_info, ulong shp, int id)
|
|
{
|
|
char buf[BUFSIZE];
|
|
|
|
msg_info->msg_queue = shp - OFFSET(msg_queue_q_perm);
|
|
|
|
/*
|
|
* cache msg_queue
|
|
*/
|
|
readmem(msg_info->msg_queue, KVADDR, buf, SIZE(msg_queue),
|
|
"msg_queue", FAULT_ON_ERROR);
|
|
|
|
msg_info->key = INT(buf + OFFSET(msg_queue_q_perm) +
|
|
OFFSET(kern_ipc_perm_key));
|
|
|
|
if (VALID_MEMBER(msg_queue_q_id))
|
|
msg_info->msgid = INT(buf + OFFSET(msg_queue_q_id));
|
|
else if (VALID_MEMBER(kern_ipc_perm_id))
|
|
msg_info->msgid = INT(buf + OFFSET(msg_queue_q_perm) +
|
|
OFFSET(kern_ipc_perm_id));
|
|
else {
|
|
ulong seq;
|
|
seq = ULONG(buf + OFFSET(msg_queue_q_perm) +
|
|
OFFSET(kern_ipc_perm_seq));
|
|
msg_info->msgid = ipcs_table.seq_multiplier * seq + id;
|
|
}
|
|
|
|
msg_info->uid = UINT(buf + OFFSET(msg_queue_q_perm) +
|
|
OFFSET(kern_ipc_perm_uid));
|
|
|
|
if (BITS32())
|
|
msg_info->perms = USHORT(buf + OFFSET(msg_queue_q_perm) +
|
|
OFFSET(kern_ipc_perm_mode));
|
|
else
|
|
msg_info->perms = UINT(buf + OFFSET(msg_queue_q_perm) +
|
|
OFFSET(kern_ipc_perm_mode));
|
|
|
|
msg_info->bytes = ULONG(buf + OFFSET(msg_queue_q_cbytes));
|
|
|
|
msg_info->messages = ULONG(buf + OFFSET(msg_queue_q_qnum));
|
|
|
|
msg_info->deleted = UINT(buf + OFFSET(msg_queue_q_perm) +
|
|
OFFSET(kern_ipc_perm_deleted));
|
|
}
|
|
|
|
/*
|
|
* get rss & swap related to every shared memory, and get the total number of rss
|
|
* & swap
|
|
*/
|
|
static void
|
|
add_rss_swap(ulong inode_p, int hugepage, ulong *rss, ulong *swap)
|
|
{
|
|
unsigned long mapping_p, nr_pages;
|
|
|
|
readmem(inode_p + OFFSET(inode_i_mapping), KVADDR, &mapping_p,
|
|
sizeof(ulong), "inode.i_mapping", FAULT_ON_ERROR);
|
|
readmem(mapping_p + OFFSET(address_space_nrpages), KVADDR, &nr_pages,
|
|
sizeof(ulong), "address_space.nrpages",
|
|
FAULT_ON_ERROR);
|
|
|
|
if (hugepage) {
|
|
unsigned long pages_per_hugepage;
|
|
if (VALID_SIZE(hstate)) {
|
|
unsigned long i_sb_p, hsb_p, hstate_p;
|
|
unsigned int order;
|
|
|
|
readmem(inode_p + OFFSET(inode_i_sb), KVADDR, &i_sb_p,
|
|
sizeof(ulong), "inode.i_sb",
|
|
FAULT_ON_ERROR);
|
|
readmem(i_sb_p + OFFSET(super_block_s_fs_info),
|
|
KVADDR, &hsb_p, sizeof(ulong),
|
|
"super_block.s_fs_info", FAULT_ON_ERROR);
|
|
readmem(hsb_p + OFFSET(hugetlbfs_sb_info_hstate),
|
|
KVADDR, &hstate_p, sizeof(ulong),
|
|
"hugetlbfs_sb_info.hstate", FAULT_ON_ERROR);
|
|
readmem(hstate_p + OFFSET(hstate_order), KVADDR,
|
|
&order, sizeof(uint), "hstate.order",
|
|
FAULT_ON_ERROR);
|
|
pages_per_hugepage = 1 << order;
|
|
} else {
|
|
unsigned long hpage_shift;
|
|
/*
|
|
* HPAGE_SHIFT is 21 after commit 83a5101b
|
|
* (kernel > 2.6.24)
|
|
*/
|
|
if (THIS_KERNEL_VERSION > LINUX(2, 6, 24)) {
|
|
hpage_shift = 21;
|
|
} else {
|
|
/*
|
|
* HPAGE_SHIFT:
|
|
* x86(PAE): 21
|
|
* x86(no PAE): 22
|
|
* x86_64: 21
|
|
*/
|
|
if ((machine_type("X86") &&
|
|
!(machdep->flags & PAE)))
|
|
hpage_shift = 22;
|
|
else
|
|
hpage_shift = 21;
|
|
}
|
|
pages_per_hugepage = (1 << hpage_shift) / PAGESIZE();
|
|
}
|
|
*rss += pages_per_hugepage * nr_pages;
|
|
} else {
|
|
unsigned long swapped;
|
|
|
|
*rss += nr_pages;
|
|
readmem(inode_p - OFFSET(shmem_inode_info_vfs_inode) +
|
|
OFFSET(shmem_inode_info_swapped), KVADDR,
|
|
&swapped, sizeof(ulong), "shmem_inode_info.swapped",
|
|
FAULT_ON_ERROR);
|
|
*swap += swapped;
|
|
}
|
|
}
|
|
|
|
static int
|
|
is_file_hugepages(ulong file_p)
|
|
{
|
|
unsigned long f_op, sfd_p;
|
|
|
|
again:
|
|
readmem(file_p + OFFSET(file_f_op), KVADDR, &f_op, sizeof(ulong),
|
|
"file.f_op", FAULT_ON_ERROR);
|
|
if (f_op == ipcs_table.hugetlbfs_f_op_addr)
|
|
return 1;
|
|
|
|
if (ipcs_table.use_shm_f_op) {
|
|
if (ipcs_table.shm_f_op_huge_addr != -1) {
|
|
if (f_op == ipcs_table.shm_f_op_huge_addr)
|
|
return 1;
|
|
} else {
|
|
if (f_op == ipcs_table.shm_f_op_addr) {
|
|
readmem(file_p +
|
|
OFFSET(file_private_data),
|
|
KVADDR, &sfd_p, sizeof(ulong),
|
|
"file.private_data", FAULT_ON_ERROR);
|
|
readmem(sfd_p +
|
|
OFFSET(shm_file_data_file),
|
|
KVADDR, &file_p, sizeof(ulong),
|
|
"shm_file_data.file", FAULT_ON_ERROR);
|
|
goto again;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|