mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2025-01-27 18:34:22 +03:00
First commit of lustre style internal debug support. These
changes bring over everything lustre had for debugging with two exceptions. I dropped by the debug daemon and upcalls just because it made things a little easier. They can be readded easily enough if we feel they are needed. Everything compiles and seems to work on first inspection but I suspect there are a handful of issues still lingering which I'll be sorting out right away. I just wanted to get all these changes commited and safe. I'm getting a little paranoid about losing them. git-svn-id: https://outreach.scidac.gov/svn/spl/trunk@75 7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c
This commit is contained in:
parent
55152ebbb4
commit
57d1b18858
@ -1,7 +1,10 @@
|
|||||||
DEFAULT_INCLUDES = -I. -I.. -I../lib
|
DEFAULT_INCLUDES = -I. -I.. -I../lib
|
||||||
AM_CFLAGS = -g -O2 -W -Wall -Wstrict-prototypes -Wshadow
|
AM_CFLAGS = -g -O2 -W -Wall -Wstrict-prototypes -Wshadow -D__USE_LARGEFILE64
|
||||||
|
|
||||||
|
sbin_PROGRAMS = spl splat
|
||||||
|
|
||||||
|
spl_SOURCES = spl.c
|
||||||
|
|
||||||
sbin_PROGRAMS = splat
|
|
||||||
splat_SOURCES = splat.c
|
splat_SOURCES = splat.c
|
||||||
splat_LDFLAGS = $(top_builddir)/lib/libcommon.la
|
splat_LDFLAGS = $(top_builddir)/lib/libcommon.la
|
||||||
|
|
||||||
|
217
cmd/spl.c
Normal file
217
cmd/spl.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "../include/spl-ctl.h"
|
||||||
|
|
||||||
|
static int spl_debug_mask = ~0;
|
||||||
|
static int spl_debug_subsystem = ~0;
|
||||||
|
|
||||||
|
/* all strings nul-terminated; only the struct and hdr need to be freed */
|
||||||
|
struct dbg_line {
|
||||||
|
struct spl_debug_header *hdr;
|
||||||
|
char *file;
|
||||||
|
char *fn;
|
||||||
|
char *text;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmp_rec(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
struct dbg_line *d1 = *(struct dbg_line **)p1;
|
||||||
|
struct dbg_line *d2 = *(struct dbg_line **)p2;
|
||||||
|
|
||||||
|
if (d1->hdr->ph_sec < d2->hdr->ph_sec)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (d1->hdr->ph_sec == d2->hdr->ph_sec &&
|
||||||
|
d1->hdr->ph_usec < d2->hdr->ph_usec)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (d1->hdr->ph_sec == d2->hdr->ph_sec &&
|
||||||
|
d1->hdr->ph_usec == d2->hdr->ph_usec)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_rec(struct dbg_line **linev, int used, FILE *out)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < used; i++) {
|
||||||
|
struct dbg_line *line = linev[i];
|
||||||
|
struct spl_debug_header *hdr = line->hdr;
|
||||||
|
|
||||||
|
fprintf(out, "%08x:%08x:%u:%u.%06llu:%u:%u:(%s:%u:%s()) %s",
|
||||||
|
hdr->ph_subsys, hdr->ph_mask, hdr->ph_cpu_id,
|
||||||
|
hdr->ph_sec, (unsigned long long)hdr->ph_usec,
|
||||||
|
hdr->ph_stack, hdr->ph_pid, line->file,
|
||||||
|
hdr->ph_line_num, line->fn, line->text);
|
||||||
|
free(line->hdr);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(linev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_rec(struct dbg_line *line, struct dbg_line ***linevp, int *lenp, int used)
|
||||||
|
{
|
||||||
|
struct dbg_line **linev = *linevp;
|
||||||
|
|
||||||
|
if (used == *lenp) {
|
||||||
|
int nlen = *lenp + 512;
|
||||||
|
int nsize = nlen * sizeof(struct dbg_line *);
|
||||||
|
|
||||||
|
linev = *linevp ? realloc(*linevp, nsize) : malloc(nsize);
|
||||||
|
if (!linev)
|
||||||
|
return 0;
|
||||||
|
*linevp = linev;
|
||||||
|
*lenp = nlen;
|
||||||
|
}
|
||||||
|
linev[used] = line;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_buffer(FILE *in, FILE *out)
|
||||||
|
{
|
||||||
|
struct dbg_line *line;
|
||||||
|
struct spl_debug_header *hdr;
|
||||||
|
char buf[4097], *p;
|
||||||
|
unsigned long dropped = 0, kept = 0;
|
||||||
|
struct dbg_line **linev = NULL;
|
||||||
|
const int phl = sizeof(hdr->ph_len);
|
||||||
|
const int phf = sizeof(hdr->ph_flags);
|
||||||
|
int rc, linev_len = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
rc = fread(buf, phl + phf, 1, in);
|
||||||
|
if (rc <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
hdr = (void *)buf;
|
||||||
|
if (hdr->ph_len == 0)
|
||||||
|
break;
|
||||||
|
if (hdr->ph_len > 4094) {
|
||||||
|
fprintf(stderr, "unexpected large record: %d bytes. "
|
||||||
|
"aborting.\n", hdr->ph_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fread(buf + phl + phf, 1, hdr->ph_len - phl - phf, in);
|
||||||
|
if (rc <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (hdr->ph_mask &&
|
||||||
|
(!(spl_debug_subsystem & hdr->ph_subsys) ||
|
||||||
|
(!(spl_debug_mask & hdr->ph_mask)))) {
|
||||||
|
dropped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
line = malloc(sizeof(*line));
|
||||||
|
if (line == NULL) {
|
||||||
|
fprintf(stderr, "malloc failed; printing accumulated "
|
||||||
|
"records and exiting.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line->hdr = malloc(hdr->ph_len + 1);
|
||||||
|
if (line->hdr == NULL) {
|
||||||
|
free(line);
|
||||||
|
fprintf(stderr, "malloc failed; printing accumulated "
|
||||||
|
"records and exiting.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = (void *)line->hdr;
|
||||||
|
memcpy(line->hdr, buf, hdr->ph_len);
|
||||||
|
p[hdr->ph_len] = '\0';
|
||||||
|
|
||||||
|
p += sizeof(*hdr);
|
||||||
|
line->file = p;
|
||||||
|
p += strlen(line->file) + 1;
|
||||||
|
line->fn = p;
|
||||||
|
p += strlen(line->fn) + 1;
|
||||||
|
line->text = p;
|
||||||
|
|
||||||
|
if (!add_rec(line, &linev, &linev_len, kept)) {
|
||||||
|
fprintf(stderr, "malloc failed; printing accumulated "
|
||||||
|
"records and exiting.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
kept++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linev) {
|
||||||
|
qsort(linev, kept, sizeof(struct dbg_line *), cmp_rec);
|
||||||
|
print_rec(linev, kept, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Debug log: %lu lines, %lu kept, %lu dropped.\n",
|
||||||
|
dropped + kept, kept, dropped);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fdin, fdout;
|
||||||
|
FILE *in, *out = stdout;
|
||||||
|
int rc, o_lf = 0;
|
||||||
|
|
||||||
|
if (argc > 3 || argc < 2) {
|
||||||
|
fprintf(stderr, "usage: %s <input> [output]\n", argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __USE_LARGEFILE64
|
||||||
|
o_lf = O_LARGEFILE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fdin = open(argv[1], O_RDONLY | o_lf);
|
||||||
|
if (fdin == -1) {
|
||||||
|
fprintf(stderr, "open(%s) failed: %s\n", argv[1],
|
||||||
|
strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
in = fdopen(fdin, "r");
|
||||||
|
if (in == NULL) {
|
||||||
|
fprintf(stderr, "fopen(%s) failed: %s\n", argv[1],
|
||||||
|
strerror(errno));
|
||||||
|
close(fdin);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (argc > 2) {
|
||||||
|
fdout = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY | o_lf, 0600);
|
||||||
|
if (fdout == -1) {
|
||||||
|
fprintf(stderr, "open(%s) failed: %s\n", argv[2],
|
||||||
|
strerror(errno));
|
||||||
|
fclose(in);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
out = fdopen(fdout, "w");
|
||||||
|
if (out == NULL) {
|
||||||
|
fprintf(stderr, "fopen(%s) failed: %s\n", argv[2],
|
||||||
|
strerror(errno));
|
||||||
|
fclose(in);
|
||||||
|
close(fdout);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = parse_buffer(in, out);
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
if (out != stdout)
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
@ -1,11 +1,318 @@
|
|||||||
#ifndef _SPL_DEBUG_H
|
#ifndef _SPL_DEBUG_H
|
||||||
#define _SPL_DEBUG_H
|
#define _SPL_DEBUG_H
|
||||||
|
|
||||||
|
#include <linux/sched.h> /* THREAD_SIZE */
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
|
||||||
extern unsigned long spl_debug_mask;
|
extern unsigned long spl_debug_mask;
|
||||||
extern unsigned long spl_debug_subsys;
|
extern unsigned long spl_debug_subsys;
|
||||||
|
|
||||||
void __dprintf(const char *file, const char *func, int line, const char *fmt, ...);
|
#define S_UNDEFINED 0x00000001
|
||||||
void spl_set_debug_mask(unsigned long mask);
|
#define S_ATOMIC 0x00000002
|
||||||
void spl_set_debug_subsys(unsigned long mask);
|
#define S_KOBJ 0x00000004
|
||||||
|
#define S_VNODE 0x00000008
|
||||||
|
#define S_TIME 0x00000010
|
||||||
|
#define S_RWLOCK 0x00000020
|
||||||
|
#define S_THREAD 0x00000040
|
||||||
|
#define S_CONDVAR 0x00000080
|
||||||
|
#define S_MUTEX 0x00000100
|
||||||
|
#define S_RNG 0x00000200
|
||||||
|
#define S_TASKQ 0x00000400
|
||||||
|
#define S_KMEM 0x00000800
|
||||||
|
#define S_DEBUG 0x00001000
|
||||||
|
#define S_GENERIC 0x00002000
|
||||||
|
#define S_PROC 0x00004000
|
||||||
|
|
||||||
|
#define D_TRACE 0x00000001
|
||||||
|
#define D_INFO 0x00000002
|
||||||
|
#define D_WARNING 0x00000004
|
||||||
|
#define D_ERROR 0x00000008
|
||||||
|
#define D_EMERG 0x00000010
|
||||||
|
#define D_CONSOLE 0x00000020
|
||||||
|
#define D_IOCTL 0x00000040
|
||||||
|
#define D_DPRINTF 0x00000080
|
||||||
|
#define D_OTHER 0x00000100
|
||||||
|
|
||||||
|
#define D_CANTMASK (D_ERROR | D_EMERG | D_WARNING | D_CONSOLE)
|
||||||
|
#define DEBUG_SUBSYSTEM S_UNDEFINED
|
||||||
|
|
||||||
|
int debug_init(void);
|
||||||
|
void debug_fini(void);
|
||||||
|
int spl_debug_mask2str(char *str, int size, unsigned long mask, int is_subsys);
|
||||||
|
int spl_debug_str2mask(unsigned long *mask, const char *str, int is_subsys);
|
||||||
|
|
||||||
|
extern unsigned long spl_debug_subsys;
|
||||||
|
extern unsigned long spl_debug_mask;
|
||||||
|
extern unsigned long spl_debug_printk;
|
||||||
|
extern int spl_debug_mb;
|
||||||
|
extern unsigned int spl_debug_binary;
|
||||||
|
extern unsigned int spl_debug_catastrophe;
|
||||||
|
extern unsigned int spl_debug_panic_on_bug;
|
||||||
|
extern char spl_debug_file_path[PATH_MAX];
|
||||||
|
extern unsigned int spl_console_ratelimit;
|
||||||
|
extern long spl_console_max_delay;
|
||||||
|
extern long spl_console_min_delay;
|
||||||
|
extern unsigned int spl_console_backoff;
|
||||||
|
extern unsigned int spl_debug_stack;
|
||||||
|
|
||||||
|
#define TCD_MAX_PAGES (5 << (20 - PAGE_SHIFT))
|
||||||
|
#define TCD_STOCK_PAGES (TCD_MAX_PAGES)
|
||||||
|
#define TRACE_CONSOLE_BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
#define SPL_DEFAULT_MAX_DELAY (600 * HZ)
|
||||||
|
#define SPL_DEFAULT_MIN_DELAY ((HZ + 1) / 2)
|
||||||
|
#define SPL_DEFAULT_BACKOFF 2
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned long cdls_next;
|
||||||
|
int cdls_count;
|
||||||
|
long cdls_delay;
|
||||||
|
} spl_debug_limit_state_t;
|
||||||
|
|
||||||
|
/* Three trace data types */
|
||||||
|
typedef enum {
|
||||||
|
TCD_TYPE_PROC,
|
||||||
|
TCD_TYPE_SOFTIRQ,
|
||||||
|
TCD_TYPE_IRQ,
|
||||||
|
TCD_TYPE_MAX
|
||||||
|
} tcd_type_t;
|
||||||
|
|
||||||
|
union trace_data_union {
|
||||||
|
struct trace_cpu_data {
|
||||||
|
/* pages with trace records not yet processed by tracefiled */
|
||||||
|
struct list_head tcd_pages;
|
||||||
|
/* number of pages on ->tcd_pages */
|
||||||
|
unsigned long tcd_cur_pages;
|
||||||
|
/* Max number of pages allowed on ->tcd_pages */
|
||||||
|
unsigned long tcd_max_pages;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* preallocated pages to write trace records into. Pages from
|
||||||
|
* ->tcd_stock_pages are moved to ->tcd_pages by spl_debug_msg().
|
||||||
|
*
|
||||||
|
* This list is necessary, because on some platforms it's
|
||||||
|
* impossible to perform efficient atomic page allocation in a
|
||||||
|
* non-blockable context.
|
||||||
|
*
|
||||||
|
* Such platforms fill ->tcd_stock_pages "on occasion", when
|
||||||
|
* tracing code is entered in blockable context.
|
||||||
|
*
|
||||||
|
* trace_get_tage_try() tries to get a page from
|
||||||
|
* ->tcd_stock_pages first and resorts to atomic page
|
||||||
|
* allocation only if this queue is empty. ->tcd_stock_pages
|
||||||
|
* is replenished when tracing code is entered in blocking
|
||||||
|
* context (darwin-tracefile.c:trace_get_tcd()). We try to
|
||||||
|
* maintain TCD_STOCK_PAGES (40 by default) pages in this
|
||||||
|
* queue. Atomic allocation is only required if more than
|
||||||
|
* TCD_STOCK_PAGES pagesful are consumed by trace records all
|
||||||
|
* emitted in non-blocking contexts. Which is quite unlikely.
|
||||||
|
*/
|
||||||
|
struct list_head tcd_stock_pages;
|
||||||
|
/* number of pages on ->tcd_stock_pages */
|
||||||
|
unsigned long tcd_cur_stock_pages;
|
||||||
|
|
||||||
|
unsigned short tcd_shutting_down;
|
||||||
|
unsigned short tcd_cpu;
|
||||||
|
unsigned short tcd_type;
|
||||||
|
/* The factors to share debug memory. */
|
||||||
|
unsigned short tcd_pages_factor;
|
||||||
|
} tcd;
|
||||||
|
char __pad[L1_CACHE_ALIGN(sizeof(struct trace_cpu_data))];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern union trace_data_union (*trace_data[TCD_TYPE_MAX])[NR_CPUS];
|
||||||
|
|
||||||
|
#define tcd_for_each(tcd, i, j) \
|
||||||
|
for (i = 0; trace_data[i] != NULL; i++) \
|
||||||
|
for (j = 0, ((tcd) = &(*trace_data[i])[j].tcd); \
|
||||||
|
j < num_possible_cpus(); j++, (tcd) = &(*trace_data[i])[j].tcd)
|
||||||
|
|
||||||
|
#define tcd_for_each_type_lock(tcd, i) \
|
||||||
|
for (i = 0; trace_data[i] && \
|
||||||
|
(tcd = &(*trace_data[i])[smp_processor_id()].tcd) && \
|
||||||
|
trace_lock_tcd(tcd); trace_unlock_tcd(tcd), i++)
|
||||||
|
|
||||||
|
struct trace_page {
|
||||||
|
struct page * page; /* page itself */
|
||||||
|
struct list_head linkage; /* Used by lists in trace_data_union */
|
||||||
|
unsigned int used; /* number of bytes used within this page */
|
||||||
|
unsigned short cpu; /* cpu that owns this page */
|
||||||
|
unsigned short type; /* type(context) of this page */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct page_collection {
|
||||||
|
struct list_head pc_pages;
|
||||||
|
spinlock_t pc_lock;
|
||||||
|
int pc_want_daemon_pages;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ASSERTION that is safe to use within the debug system */
|
||||||
|
#define __ASSERT(cond) \
|
||||||
|
do { \
|
||||||
|
if (unlikely(!(cond))) { \
|
||||||
|
printk(KERN_ERR "ASSERTION("#cond") failed"); \
|
||||||
|
BUG(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define __ASSERT_TAGE_INVARIANT(tage) \
|
||||||
|
do { \
|
||||||
|
__ASSERT(tage != NULL); \
|
||||||
|
__ASSERT(tage->page != NULL); \
|
||||||
|
__ASSERT(tage->used <= PAGE_SIZE); \
|
||||||
|
__ASSERT(page_count(tage->page) > 0); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ASSERTION that will debug log used outside the debug sysytem */
|
||||||
|
#define ASSERT(cond) \
|
||||||
|
do { \
|
||||||
|
if (unlikely(!(cond))) { \
|
||||||
|
spl_debug_msg(NULL, DEBUG_SUBSYSTEM, D_EMERG, \
|
||||||
|
__FILE__, __FUNCTION__, __LINE__, \
|
||||||
|
"ASSERTION(" #cond ") failed\n"); \
|
||||||
|
spl_debug_bug(__FILE__, __FUNCTION__, __LINE__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERTF(cond, fmt, a...) \
|
||||||
|
do { \
|
||||||
|
if (unlikely(!(cond))) { \
|
||||||
|
spl_debug_msg(NULL, DEBUG_SUBSYSTEM, D_EMERG, \
|
||||||
|
__FILE__, __FUNCTION__, __LINE__, \
|
||||||
|
"ASSERTION(" #cond ") failed:" fmt, \
|
||||||
|
## a); \
|
||||||
|
spl_debug_bug(__FILE__, __FUNCTION__, __LINE__) \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VERIFY3_IMPL(LEFT, OP, RIGHT, TYPE, FMT, CAST) \
|
||||||
|
do { \
|
||||||
|
const TYPE __left = (TYPE)(LEFT); \
|
||||||
|
const TYPE __right = (TYPE)(RIGHT); \
|
||||||
|
if (!(__left OP __right)) { \
|
||||||
|
spl_debug_msg(NULL, DEBUG_SUBSYSTEM, D_EMERG, \
|
||||||
|
__FILE__, __FUNCTION__, __LINE__, \
|
||||||
|
"VERIFY3(" FMT " " #OP " " FMT ")\n", \
|
||||||
|
CAST __left, CAST __right); \
|
||||||
|
spl_debug_bug(__FILE__, __FUNCTION__, __LINE__) \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VERIFY3S(x,y,z) VERIFY3_IMPL(x, y, z, int64_t, "%ld", (long))
|
||||||
|
#define VERIFY3U(x,y,z) VERIFY3_IMPL(x, y, z, uint64_t, "%lu", (unsigned long))
|
||||||
|
#define VERIFY3P(x,y,z) VERIFY3_IMPL(x, y, z, uintptr_t, "%p", (void *))
|
||||||
|
|
||||||
|
#define ASSERT3S(x,y,z) VERIFY3S(x, y, z)
|
||||||
|
#define ASSERT3U(x,y,z) VERIFY3U(x, y, z)
|
||||||
|
#define ASSERT3P(x,y,z) VERIFY3P(x, y, z)
|
||||||
|
|
||||||
|
#define VERIFY(x) ASSERT(x)
|
||||||
|
#define SBUG() spl_debug_bug(__FILE__, __FUNCTION__, __LINE__);
|
||||||
|
|
||||||
|
#define spl_debug_msg(cdls, subsys, mask, file, fn, line, format, a...) \
|
||||||
|
spl_debug_vmsg(cdls, subsys, mask, file, fn, \
|
||||||
|
line, NULL, NULL, format, ##a)
|
||||||
|
|
||||||
|
#ifdef __ia64__
|
||||||
|
#define CDEBUG_STACK() (THREAD_SIZE - \
|
||||||
|
((unsigned long)__builtin_dwarf_cfa() & \
|
||||||
|
(THREAD_SIZE - 1)))
|
||||||
|
#else
|
||||||
|
#define CDEBUG_STACK() (THREAD_SIZE - \
|
||||||
|
((unsigned long)__builtin_frame_address(0) & \
|
||||||
|
(THREAD_SIZE - 1)))
|
||||||
|
# endif /* __ia64__ */
|
||||||
|
|
||||||
|
#define __CHECK_STACK(file, func, line) \
|
||||||
|
do { \
|
||||||
|
unsigned long _stack = CDEBUG_STACK(); \
|
||||||
|
\
|
||||||
|
if (_stack > (3*THREAD_SIZE/4) && _stack > spl_debug_stack) { \
|
||||||
|
spl_debug_stack = _stack; \
|
||||||
|
spl_debug_msg(NULL, D_TRACE, D_WARNING, \
|
||||||
|
file, func, line, \
|
||||||
|
"Exceeded maximum safe stack " \
|
||||||
|
"%lu/%lu\n", _stack, THREAD_SIZE); \
|
||||||
|
__ASSERT(0); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CHECK_STACK()__CHECK_STACK(__FILE__, __func__, __LINE__)
|
||||||
|
|
||||||
|
#define __CDEBUG(cdls, subsys, mask, format, a...) \
|
||||||
|
do { \
|
||||||
|
CHECK_STACK(); \
|
||||||
|
\
|
||||||
|
if (((mask) & D_CANTMASK) != 0 || \
|
||||||
|
((spl_debug_mask & (mask)) != 0 && \
|
||||||
|
(spl_debug_subsys & (subsys)) != 0)) \
|
||||||
|
spl_debug_msg(cdls, subsys, mask, \
|
||||||
|
__FILE__, __FUNCTION__, __LINE__, \
|
||||||
|
format, ## a); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CDEBUG(mask, format, a...) \
|
||||||
|
__CDEBUG(NULL, DEBUG_SUBSYSTEM, mask, format, ## a)
|
||||||
|
|
||||||
|
#define __CDEBUG_LIMIT(subsys, mask, format, a...) \
|
||||||
|
do { \
|
||||||
|
static spl_debug_limit_state_t cdls; \
|
||||||
|
\
|
||||||
|
__CDEBUG(&cdls, subsys, mask, format, ## a); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CDEBUG_LIMIT(mask, format, a...) \
|
||||||
|
__CDEBUG_LIMIT(DEBUG_SUBSYSTEM, mask, format, ## a)
|
||||||
|
|
||||||
|
#define dprintf(fmt, a...) CDEBUG_LIMIT(D_INFO, fmt, ## a)
|
||||||
|
#define CWARN(fmt, a...) CDEBUG_LIMIT(D_WARNING, fmt, ## a)
|
||||||
|
#define CERROR(fmt, a...) CDEBUG_LIMIT(D_ERROR, fmt, ## a)
|
||||||
|
#define CEMERG(fmt, a...) CDEBUG_LIMIT(D_EMERG, fmt, ## a)
|
||||||
|
#define CONSOLE(mask, fmt, a...) CDEBUG(D_CONSOLE | (mask), fmt, ## a)
|
||||||
|
|
||||||
|
#define GOTO(label, rc) \
|
||||||
|
do { \
|
||||||
|
long GOTO__ret = (long)(rc); \
|
||||||
|
CDEBUG(D_TRACE,"Process leaving via %s (rc=%lu : %ld : %lx)\n", \
|
||||||
|
#label, (unsigned long)GOTO__ret, (signed long)GOTO__ret,\
|
||||||
|
(signed long)GOTO__ret); \
|
||||||
|
goto label; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define RETURN(rc) \
|
||||||
|
do { \
|
||||||
|
typeof(rc) RETURN__ret = (rc); \
|
||||||
|
CDEBUG(D_TRACE, "Process leaving (rc=%lu : %ld : %lx)\n", \
|
||||||
|
(long)RETURN__ret, (long)RETURN__ret, (long)RETURN__ret);\
|
||||||
|
return RETURN__ret; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ENTRY \
|
||||||
|
do { \
|
||||||
|
CDEBUG(D_TRACE, "Process entered\n"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EXIT \
|
||||||
|
do { \
|
||||||
|
CDEBUG(D_TRACE, "Process leaving\n"); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
extern int spl_debug_vmsg(spl_debug_limit_state_t *cdls, int subsys, int mask,
|
||||||
|
const char *file, const char *fn, const int line,
|
||||||
|
const char *format1, va_list args, const char *format2, ...);
|
||||||
|
|
||||||
|
extern unsigned long spl_debug_set_mask(unsigned long mask);
|
||||||
|
extern unsigned long spl_debug_get_mask(void);
|
||||||
|
extern unsigned long spl_debug_set_subsys(unsigned long mask);
|
||||||
|
extern unsigned long spl_debug_get_subsys(void);
|
||||||
|
extern int spl_debug_set_mb(int mb);
|
||||||
|
extern int spl_debug_get_mb(void);
|
||||||
|
|
||||||
|
extern int spl_debug_dumplog(void);
|
||||||
|
extern void spl_debug_dumpstack(struct task_struct *tsk);
|
||||||
|
extern void spl_debug_bug(char *file, const char *func, const int line);
|
||||||
|
|
||||||
|
extern int spl_debug_clear_buffer(void);
|
||||||
|
extern int spl_debug_mark_buffer(char *text);
|
||||||
|
|
||||||
#endif /* SPL_DEBUG_H */
|
#endif /* SPL_DEBUG_H */
|
||||||
|
9
include/sys/proc.h
Normal file
9
include/sys/proc.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef _SPL_PROC_H
|
||||||
|
#define _SPL_PROC_H
|
||||||
|
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
|
||||||
|
int proc_init(void);
|
||||||
|
void proc_fini(void);
|
||||||
|
|
||||||
|
#endif /* SPL_PROC_H */
|
@ -6,6 +6,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <sys/debug.h>
|
||||||
#include <sys/varargs.h>
|
#include <sys/varargs.h>
|
||||||
#include <sys/zone.h>
|
#include <sys/zone.h>
|
||||||
#include <sys/signal.h>
|
#include <sys/signal.h>
|
||||||
@ -73,26 +74,6 @@ extern "C" {
|
|||||||
#define bzero(ptr,size) memset(ptr,0,size)
|
#define bzero(ptr,size) memset(ptr,0,size)
|
||||||
#define bcopy(src,dest,size) memcpy(dest,src,size)
|
#define bcopy(src,dest,size) memcpy(dest,src,size)
|
||||||
#define bcmp(src,dest,size) memcmp((src), (dest), (size_t)(size))
|
#define bcmp(src,dest,size) memcmp((src), (dest), (size_t)(size))
|
||||||
#define ASSERT(x) BUG_ON(!(x))
|
|
||||||
#define VERIFY(x) ASSERT(x)
|
|
||||||
|
|
||||||
#define VERIFY3_IMPL(LEFT, OP, RIGHT, TYPE, FMT, CAST) do { \
|
|
||||||
const TYPE __left = (TYPE)(LEFT); \
|
|
||||||
const TYPE __right = (TYPE)(RIGHT); \
|
|
||||||
if (!(__left OP __right)) { \
|
|
||||||
printk("spl: Error VERIFY3(" FMT " " #OP " " FMT ")\n", \
|
|
||||||
CAST __left, CAST __right); \
|
|
||||||
BUG(); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define VERIFY3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t, "%ld", (long))
|
|
||||||
#define VERIFY3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t, "%lu", (unsigned long))
|
|
||||||
#define VERIFY3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t, "%p", (void *))
|
|
||||||
|
|
||||||
#define ASSERT3S(x, y, z) VERIFY3S(x, y, z)
|
|
||||||
#define ASSERT3U(x, y, z) VERIFY3U(x, y, z)
|
|
||||||
#define ASSERT3P(x, y, z) VERIFY3P(x, y, z)
|
|
||||||
|
|
||||||
/* Dtrace probes do not exist in the linux kernel */
|
/* Dtrace probes do not exist in the linux kernel */
|
||||||
|
|
||||||
@ -117,12 +98,13 @@ extern "C" {
|
|||||||
#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) ((void)0)
|
#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) ((void)0)
|
||||||
|
|
||||||
/* Missing globals */
|
/* Missing globals */
|
||||||
|
extern unsigned long spl_hostid;
|
||||||
|
extern char spl_hw_serial[11];
|
||||||
extern int p0;
|
extern int p0;
|
||||||
extern char hw_serial[11];
|
|
||||||
|
|
||||||
/* Missing misc functions */
|
/* Missing misc functions */
|
||||||
extern int highbit(unsigned long i);
|
extern int highbit(unsigned long i);
|
||||||
extern int ddi_strtoul(const char *hw_serial, char **nptr,
|
extern int ddi_strtoul(const char *str, char **nptr,
|
||||||
int base, unsigned long *result);
|
int base, unsigned long *result);
|
||||||
|
|
||||||
#define makedevice(maj,min) makedev(maj,min)
|
#define makedevice(maj,min) makedev(maj,min)
|
||||||
|
@ -9,6 +9,8 @@ CPPFLAGS += @KERNELCPPFLAGS@
|
|||||||
# Solaris porting layer module
|
# Solaris porting layer module
|
||||||
obj-m := spl.o
|
obj-m := spl.o
|
||||||
|
|
||||||
|
spl-objs += spl-debug.o
|
||||||
|
spl-objs += spl-proc.o
|
||||||
spl-objs += spl-kmem.o
|
spl-objs += spl-kmem.o
|
||||||
spl-objs += spl-thread.o
|
spl-objs += spl-thread.o
|
||||||
spl-objs += spl-taskq.o
|
spl-objs += spl-taskq.o
|
||||||
|
1293
modules/spl/spl-debug.c
Normal file
1293
modules/spl/spl-debug.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,32 +3,21 @@
|
|||||||
#include <sys/vnode.h>
|
#include <sys/vnode.h>
|
||||||
#include <sys/kmem.h>
|
#include <sys/kmem.h>
|
||||||
#include <sys/debug.h>
|
#include <sys/debug.h>
|
||||||
#include <linux/proc_fs.h>
|
#include <sys/proc.h>
|
||||||
#include <linux/kmod.h>
|
#include <linux/kmod.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
/*
|
#ifdef DEBUG_SUBSYSTEM
|
||||||
* Generic support
|
#undef DEBUG_SUBSYSTEM
|
||||||
*/
|
#endif
|
||||||
static char spl_debug_buffer1[1024];
|
|
||||||
static char spl_debug_buffer2[1024];
|
#define DEBUG_SUBSYSTEM S_GENERIC
|
||||||
static spinlock_t spl_debug_lock = SPIN_LOCK_UNLOCKED;
|
|
||||||
|
|
||||||
unsigned long spl_debug_mask = 0;
|
|
||||||
unsigned long spl_debug_subsys = 0xff;
|
|
||||||
unsigned long spl_hostid = 0;
|
unsigned long spl_hostid = 0;
|
||||||
char hw_serial[11] = "<none>";
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(spl_debug_mask);
|
|
||||||
EXPORT_SYMBOL(spl_debug_subsys);
|
|
||||||
EXPORT_SYMBOL(spl_hostid);
|
EXPORT_SYMBOL(spl_hostid);
|
||||||
EXPORT_SYMBOL(hw_serial);
|
|
||||||
|
|
||||||
static struct proc_dir_entry *spl_proc_root = NULL;
|
char spl_hw_serial[11] = "<none>";
|
||||||
static struct proc_dir_entry *spl_proc_debug_mask = NULL;
|
EXPORT_SYMBOL(spl_hw_serial);
|
||||||
static struct proc_dir_entry *spl_proc_debug_subsys = NULL;
|
|
||||||
static struct proc_dir_entry *spl_proc_hostid = NULL;
|
|
||||||
static struct proc_dir_entry *spl_proc_hw_serial = NULL;
|
|
||||||
|
|
||||||
int p0 = 0;
|
int p0 = 0;
|
||||||
EXPORT_SYMBOL(p0);
|
EXPORT_SYMBOL(p0);
|
||||||
@ -36,14 +25,14 @@ EXPORT_SYMBOL(p0);
|
|||||||
vmem_t *zio_alloc_arena = NULL;
|
vmem_t *zio_alloc_arena = NULL;
|
||||||
EXPORT_SYMBOL(zio_alloc_arena);
|
EXPORT_SYMBOL(zio_alloc_arena);
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
highbit(unsigned long i)
|
highbit(unsigned long i)
|
||||||
{
|
{
|
||||||
register int h = 1;
|
register int h = 1;
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
return (0);
|
RETURN(0);
|
||||||
#if BITS_PER_LONG == 64
|
#if BITS_PER_LONG == 64
|
||||||
if (i & 0xffffffff00000000ul) {
|
if (i & 0xffffffff00000000ul) {
|
||||||
h += 32; i >>= 32;
|
h += 32; i >>= 32;
|
||||||
@ -64,7 +53,7 @@ highbit(unsigned long i)
|
|||||||
if (i & 0x2) {
|
if (i & 0x2) {
|
||||||
h += 1;
|
h += 1;
|
||||||
}
|
}
|
||||||
return (h);
|
RETURN(h);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(highbit);
|
EXPORT_SYMBOL(highbit);
|
||||||
|
|
||||||
@ -76,324 +65,75 @@ ddi_strtoul(const char *str, char **nptr, int base, unsigned long *result)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ddi_strtoul);
|
EXPORT_SYMBOL(ddi_strtoul);
|
||||||
|
|
||||||
/* XXX: Not the most efficient debug function ever. This should be re-done
|
|
||||||
* as an internal per-cpu in-memory debug log accessable via /proc/. Not as
|
|
||||||
* a shared global buffer everything gets serialize though. That said I'll
|
|
||||||
* worry about performance considerations once I've dealt with correctness.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
char *sfp;
|
|
||||||
struct timeval tv;
|
|
||||||
unsigned long flags;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
sfp = strrchr(file, '/');
|
|
||||||
do_gettimeofday(&tv);
|
|
||||||
|
|
||||||
/* XXX: This is particularly bad for performance, but we need to
|
|
||||||
* disable irqs here or two __dprintf()'s may deadlock on each
|
|
||||||
* other if one if called from an irq handler. This is yet another
|
|
||||||
* reason why we really, really, need an internal debug log.
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&spl_debug_lock, flags);
|
|
||||||
memset(spl_debug_buffer1, 0, 1024);
|
|
||||||
memset(spl_debug_buffer2, 0, 1024);
|
|
||||||
|
|
||||||
snprintf(spl_debug_buffer1, 1023,
|
|
||||||
"spl: %lu.%06lu:%d:%u:%s:%d:%s(): ",
|
|
||||||
tv.tv_sec, tv.tv_usec, current->pid,
|
|
||||||
smp_processor_id(),
|
|
||||||
sfp == NULL ? file : sfp + 1,
|
|
||||||
line, func);
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsnprintf(spl_debug_buffer2, 1023, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
printk("%s%s", spl_debug_buffer1, spl_debug_buffer2);
|
|
||||||
spin_unlock_irqrestore(&spl_debug_lock, flags);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(__dprintf);
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
spl_proc_rd_generic_ul(char *page, char **start, off_t off,
|
set_hostid(void)
|
||||||
int count, int *eof, unsigned long val)
|
|
||||||
{
|
{
|
||||||
*start = page;
|
|
||||||
*eof = 1;
|
|
||||||
|
|
||||||
if (off || count > PAGE_SIZE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return snprintf(page, PAGE_SIZE, "0x%lx\n", val & 0xffffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_rd_debug_mask(char *page, char **start, off_t off,
|
|
||||||
int count, int *eof, void *data)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
spin_lock(&spl_debug_lock);
|
|
||||||
rc = spl_proc_rd_generic_ul(page, start, off, count,
|
|
||||||
eof, spl_debug_mask);
|
|
||||||
spin_unlock(&spl_debug_lock);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_rd_debug_subsys(char *page, char **start, off_t off,
|
|
||||||
int count, int *eof, void *data)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
spin_lock(&spl_debug_lock);
|
|
||||||
rc = spl_proc_rd_generic_ul(page, start, off, count,
|
|
||||||
eof, spl_debug_subsys);
|
|
||||||
spin_unlock(&spl_debug_lock);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_rd_hostid(char *page, char **start, off_t off,
|
|
||||||
int count, int *eof, void *data)
|
|
||||||
{
|
|
||||||
*start = page;
|
|
||||||
*eof = 1;
|
|
||||||
|
|
||||||
if (off || count > PAGE_SIZE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return snprintf(page, PAGE_SIZE, "%lx\n", spl_hostid & 0xffffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_rd_hw_serial(char *page, char **start, off_t off,
|
|
||||||
int count, int *eof, void *data)
|
|
||||||
{
|
|
||||||
*start = page;
|
|
||||||
*eof = 1;
|
|
||||||
|
|
||||||
if (off || count > PAGE_SIZE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
strncpy(page, hw_serial, 11);
|
|
||||||
return strlen(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_wr_generic_ul(const char *ubuf, unsigned long count,
|
|
||||||
unsigned long *val, int base)
|
|
||||||
{
|
|
||||||
char *end, kbuf[32];
|
|
||||||
|
|
||||||
if (count >= sizeof(kbuf))
|
|
||||||
return -EOVERFLOW;
|
|
||||||
|
|
||||||
if (copy_from_user(kbuf, ubuf, count))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
kbuf[count] = '\0';
|
|
||||||
*val = (int)simple_strtoul(kbuf, &end, base);
|
|
||||||
if (kbuf == end)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_wr_debug_mask(struct file *file, const char *ubuf,
|
|
||||||
unsigned long count, void *data, int mode)
|
|
||||||
{
|
|
||||||
unsigned long val;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = spl_proc_wr_generic_ul(ubuf, count, &val, 16);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
spin_lock(&spl_debug_lock);
|
|
||||||
spl_debug_mask = val;
|
|
||||||
spin_unlock(&spl_debug_lock);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_wr_debug_subsys(struct file *file, const char *ubuf,
|
|
||||||
unsigned long count, void *data, int mode)
|
|
||||||
{
|
|
||||||
unsigned long val;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = spl_proc_wr_generic_ul(ubuf, count, &val, 16);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
spin_lock(&spl_debug_lock);
|
|
||||||
spl_debug_subsys = val;
|
|
||||||
spin_unlock(&spl_debug_lock);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
spl_proc_wr_hostid(struct file *file, const char *ubuf,
|
|
||||||
unsigned long count, void *data, int mode)
|
|
||||||
{
|
|
||||||
unsigned long val;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = spl_proc_wr_generic_ul(ubuf, count, &val, 16);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
spl_hostid = val;
|
|
||||||
sprintf(hw_serial, "%lu\n", ((long)val >= 0) ? val : -val);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct proc_dir_entry *
|
|
||||||
spl_register_proc_entry(const char *name, mode_t mode,
|
|
||||||
struct proc_dir_entry *parent, void *data,
|
|
||||||
void *read_proc, void *write_proc)
|
|
||||||
{
|
|
||||||
struct proc_dir_entry *entry;
|
|
||||||
|
|
||||||
entry = create_proc_entry(name, mode, parent);
|
|
||||||
if (!entry)
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
|
|
||||||
entry->data = data;
|
|
||||||
entry->read_proc = read_proc;
|
|
||||||
entry->write_proc = write_proc;
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
} /* register_proc_entry() */
|
|
||||||
|
|
||||||
void spl_set_debug_mask(unsigned long mask) {
|
|
||||||
spin_lock(&spl_debug_lock);
|
|
||||||
spl_debug_mask = mask;
|
|
||||||
spin_unlock(&spl_debug_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(spl_set_debug_mask);
|
|
||||||
|
|
||||||
void spl_set_debug_subsys(unsigned long mask) {
|
|
||||||
spin_lock(&spl_debug_lock);
|
|
||||||
spl_debug_subsys = mask;
|
|
||||||
spin_unlock(&spl_debug_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(spl_set_debug_subsys);
|
|
||||||
|
|
||||||
static int __init spl_init(void)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
char sh_path[] = "/bin/sh";
|
char sh_path[] = "/bin/sh";
|
||||||
char *argv[] = { sh_path,
|
char *argv[] = { sh_path,
|
||||||
"-c",
|
"-c",
|
||||||
"/usr/bin/hostid >/proc/spl/hostid",
|
"/usr/bin/hostid >/proc/sys/spl/hostid",
|
||||||
NULL };
|
NULL };
|
||||||
char *envp[] = { "HOME=/",
|
char *envp[] = { "HOME=/",
|
||||||
"TERM=linux",
|
"TERM=linux",
|
||||||
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
|
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
|
||||||
NULL };
|
NULL };
|
||||||
|
|
||||||
spl_proc_root = proc_mkdir("spl", NULL);
|
/* Doing address resolution in the kernel is tricky and just
|
||||||
if (!spl_proc_root) {
|
* not a good idea in general. So to set the proper 'spl_hw_serial'
|
||||||
printk("spl: Error unable to create /proc/spl/ directory\n");
|
* use the usermodehelper support to ask '/bin/sh' to run
|
||||||
return -EINVAL;
|
* '/usr/bin/hostid' and redirect the result to /proc/sys/spl/hostid
|
||||||
|
* for us to use. It's a horific solution but it will do for now.
|
||||||
|
*/
|
||||||
|
return call_usermodehelper(sh_path, argv, envp, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_proc_debug_mask = spl_register_proc_entry("debug_mask", 0644,
|
static int __init spl_init(void)
|
||||||
spl_proc_root, NULL,
|
{
|
||||||
spl_proc_rd_debug_mask,
|
int rc = 0;
|
||||||
spl_proc_wr_debug_mask);
|
ENTRY;
|
||||||
if (IS_ERR(spl_proc_debug_mask)) {
|
|
||||||
rc = PTR_ERR(spl_proc_debug_mask);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_proc_debug_subsys = spl_register_proc_entry("debug_subsys", 0644,
|
if ((rc = debug_init()))
|
||||||
spl_proc_root, NULL,
|
RETURN(rc);
|
||||||
spl_proc_rd_debug_subsys,
|
|
||||||
spl_proc_wr_debug_subsys);
|
|
||||||
if (IS_ERR(spl_proc_debug_subsys)) {
|
|
||||||
rc = PTR_ERR(spl_proc_debug_subsys);
|
|
||||||
goto out2;
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_proc_hostid = spl_register_proc_entry("hostid", 0644,
|
|
||||||
spl_proc_root, NULL,
|
|
||||||
spl_proc_rd_hostid,
|
|
||||||
spl_proc_wr_hostid);
|
|
||||||
if (IS_ERR(spl_proc_hostid)) {
|
|
||||||
rc = PTR_ERR(spl_proc_hostid);
|
|
||||||
goto out3;
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_proc_hw_serial = spl_register_proc_entry("hw_serial", 0444,
|
|
||||||
spl_proc_root, NULL,
|
|
||||||
spl_proc_rd_hw_serial,
|
|
||||||
NULL);
|
|
||||||
if (IS_ERR(spl_proc_hw_serial)) {
|
|
||||||
rc = PTR_ERR(spl_proc_hw_serial);
|
|
||||||
goto out4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((rc = kmem_init()))
|
if ((rc = kmem_init()))
|
||||||
goto out4;
|
GOTO(out , rc);
|
||||||
|
|
||||||
if ((rc = vn_init()))
|
if ((rc = vn_init()))
|
||||||
goto out4;
|
GOTO(out2, rc);
|
||||||
|
|
||||||
/* Doing address resolution in the kernel is tricky and just
|
if ((rc = proc_init()))
|
||||||
* not a good idea in general. So to set the proper 'hw_serial'
|
GOTO(out3, rc);
|
||||||
* use the usermodehelper support to ask '/bin/sh' to run
|
|
||||||
* '/usr/bin/hostid' and redirect the result to /proc/spl/hostid
|
|
||||||
* for us to use. It's a horific solution but it will do.
|
|
||||||
*/
|
|
||||||
if ((rc = call_usermodehelper(sh_path, argv, envp, 1)))
|
|
||||||
goto out4;
|
|
||||||
|
|
||||||
printk("spl: Loaded Solaris Porting Layer v%s\n", VERSION);
|
if ((rc = set_hostid()))
|
||||||
|
GOTO(out4, rc = -EADDRNOTAVAIL);
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
CWARN("Loaded Solaris Porting Layer v%s\n", VERSION);
|
||||||
|
RETURN(rc);
|
||||||
out4:
|
out4:
|
||||||
if (spl_proc_hw_serial)
|
proc_fini();
|
||||||
remove_proc_entry("hw_serial", spl_proc_root);
|
|
||||||
out3:
|
out3:
|
||||||
if (spl_proc_hostid)
|
vn_fini();
|
||||||
remove_proc_entry("hostid", spl_proc_root);
|
|
||||||
out2:
|
out2:
|
||||||
if (spl_proc_debug_mask)
|
kmem_fini();
|
||||||
remove_proc_entry("debug_mask", spl_proc_root);
|
|
||||||
|
|
||||||
if (spl_proc_debug_subsys)
|
|
||||||
remove_proc_entry("debug_subsys", spl_proc_root);
|
|
||||||
out:
|
out:
|
||||||
remove_proc_entry("spl", NULL);
|
debug_fini();
|
||||||
|
|
||||||
return rc;
|
printk("SPL: Failed to Load Solaris Porting Layer v%s, "
|
||||||
|
"rc = %d\n", VERSION, rc);
|
||||||
|
RETURN(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spl_fini(void)
|
static void spl_fini(void)
|
||||||
{
|
{
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
CWARN("Unloaded Solaris Porting Layer v%s\n", VERSION);
|
||||||
|
proc_fini();
|
||||||
vn_fini();
|
vn_fini();
|
||||||
kmem_fini();
|
kmem_fini();
|
||||||
|
debug_fini();
|
||||||
|
|
||||||
remove_proc_entry("hw_serial", spl_proc_root);
|
EXIT;
|
||||||
remove_proc_entry("hostid", spl_proc_root);
|
|
||||||
remove_proc_entry("debug_subsys", spl_proc_root);
|
|
||||||
remove_proc_entry("debug_mask", spl_proc_root);
|
|
||||||
remove_proc_entry("spl", NULL);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(spl_init);
|
module_init(spl_init);
|
||||||
|
574
modules/spl/spl-proc.c
Normal file
574
modules/spl/spl-proc.c
Normal file
@ -0,0 +1,574 @@
|
|||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/kmod.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/sysctl.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/kmem.h>
|
||||||
|
#include <sys/debug.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG_SUBSYSTEM
|
||||||
|
#undef DEBUG_SUBSYSTEM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEBUG_SUBSYSTEM S_PROC
|
||||||
|
|
||||||
|
static struct ctl_table_header *spl_header = NULL;
|
||||||
|
static unsigned long table_min = 0;
|
||||||
|
static unsigned long table_max = ~0;
|
||||||
|
|
||||||
|
#define CTL_SPL 0x87
|
||||||
|
enum {
|
||||||
|
CTL_DEBUG_SUBSYS = 1, /* Debug subsystem */
|
||||||
|
CTL_DEBUG_MASK, /* Debug mask */
|
||||||
|
CTL_DEBUG_PRINTK, /* Force all messages to console */
|
||||||
|
CTL_DEBUG_MB, /* Debug buffer size */
|
||||||
|
CTL_DEBUG_BINARY, /* Include binary data in buffer */
|
||||||
|
CTL_DEBUG_CATASTROPHE, /* Set if we have BUG'd or panic'd */
|
||||||
|
CTL_DEBUG_PANIC_ON_BUG, /* Set if we should panic on BUG */
|
||||||
|
CTL_DEBUG_PATH, /* Dump log location */
|
||||||
|
CTL_DEBUG_DUMP, /* Dump debug buffer to file */
|
||||||
|
CTL_DEBUG_FORCE_BUG, /* Hook to force a BUG */
|
||||||
|
CTL_CONSOLE_RATELIMIT, /* Ratelimit console messages */
|
||||||
|
CTL_CONSOLE_MAX_DELAY_CS, /* Max delay at which we skip messages */
|
||||||
|
CTL_CONSOLE_MIN_DELAY_CS, /* Init delay at which we skip messages */
|
||||||
|
CTL_CONSOLE_BACKOFF, /* Delay increase factor */
|
||||||
|
CTL_STACK_SIZE, /* Max observed stack size */
|
||||||
|
#ifdef DEBUG_KMEM
|
||||||
|
CTL_KMEM_KMEMUSED, /* Crrently alloc'd kmem bytes */
|
||||||
|
CTL_KMEM_KMEMMAX, /* Max alloc'd by kmem bytes */
|
||||||
|
CTL_KMEM_VMEMUSED, /* Currently alloc'd vmem bytes */
|
||||||
|
CTL_KMEM_VMEMMAX, /* Max alloc'd by vmem bytes */
|
||||||
|
#endif
|
||||||
|
CTL_HOSTID, /* Host id reported by /usr/bin/hostid */
|
||||||
|
CTL_HW_SERIAL, /* Hardware serial number from hostid */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_copyin_string(char *kbuffer, int kbuffer_size,
|
||||||
|
const char *ubuffer, int ubuffer_size)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (ubuffer_size > kbuffer_size)
|
||||||
|
return -EOVERFLOW;
|
||||||
|
|
||||||
|
if (copy_from_user((void *)kbuffer, (void *)ubuffer, ubuffer_size))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
/* strip trailing whitespace */
|
||||||
|
size = strnlen(kbuffer, ubuffer_size);
|
||||||
|
while (size-- >= 0)
|
||||||
|
if (!isspace(kbuffer[size]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* empty string */
|
||||||
|
if (size < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* no space to terminate */
|
||||||
|
if (size == kbuffer_size)
|
||||||
|
return -EOVERFLOW;
|
||||||
|
|
||||||
|
kbuffer[size + 1] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_copyout_string(char *ubuffer, int ubuffer_size,
|
||||||
|
const char *kbuffer, char *append)
|
||||||
|
{
|
||||||
|
/* NB if 'append' != NULL, it's a single character to append to the
|
||||||
|
* copied out string - usually "\n", for /proc entries and
|
||||||
|
* (i.e. a terminating zero byte) for sysctl entries
|
||||||
|
*/
|
||||||
|
int size = MIN(strlen(kbuffer), ubuffer_size);
|
||||||
|
|
||||||
|
if (copy_to_user(ubuffer, kbuffer, size))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (append != NULL && size < ubuffer_size) {
|
||||||
|
if (copy_to_user(ubuffer + size, append, 1))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_dobitmasks(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
unsigned long *mask = table->data;
|
||||||
|
int is_subsys = (mask == &spl_debug_subsys) ? 1 : 0;
|
||||||
|
int is_printk = (mask == &spl_debug_printk) ? 1 : 0;
|
||||||
|
int size = 512, rc;
|
||||||
|
char *str;
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
str = kmem_alloc(size, KM_SLEEP);
|
||||||
|
if (str == NULL)
|
||||||
|
RETURN(-ENOMEM);
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
rc = proc_copyin_string(str, size, buffer, *lenp);
|
||||||
|
if (rc < 0)
|
||||||
|
RETURN(rc);
|
||||||
|
|
||||||
|
rc = spl_debug_str2mask(mask, str, is_subsys);
|
||||||
|
/* Always print BUG/ASSERT to console, so keep this mask */
|
||||||
|
if (is_printk)
|
||||||
|
*mask |= D_EMERG;
|
||||||
|
|
||||||
|
*ppos += *lenp;
|
||||||
|
} else {
|
||||||
|
rc = spl_debug_mask2str(str, size, *mask, is_subsys);
|
||||||
|
if (*ppos >= rc)
|
||||||
|
rc = 0;
|
||||||
|
else
|
||||||
|
rc = proc_copyout_string(buffer, *lenp,
|
||||||
|
str + *ppos, "\n");
|
||||||
|
if (rc >= 0) {
|
||||||
|
*lenp = rc;
|
||||||
|
*ppos += rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kmem_free(str, size);
|
||||||
|
RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_debug_mb(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
char str[32];
|
||||||
|
int rc, len;
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
|
||||||
|
if (rc < 0)
|
||||||
|
RETURN(rc);
|
||||||
|
|
||||||
|
rc = spl_debug_set_mb(simple_strtoul(str, NULL, 0));
|
||||||
|
*ppos += *lenp;
|
||||||
|
} else {
|
||||||
|
len = snprintf(str, sizeof(str), "%d", spl_debug_get_mb());
|
||||||
|
if (*ppos >= len)
|
||||||
|
rc = 0;
|
||||||
|
else
|
||||||
|
rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
|
||||||
|
|
||||||
|
if (rc >= 0) {
|
||||||
|
*lenp = rc;
|
||||||
|
*ppos += rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_dump_kernel(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
spl_debug_dumplog();
|
||||||
|
*ppos += *lenp;
|
||||||
|
} else {
|
||||||
|
*lenp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_force_bug(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
CERROR("Crashing due to forced BUG\n");
|
||||||
|
BUG();
|
||||||
|
/* Unreachable */
|
||||||
|
} else {
|
||||||
|
*lenp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_console_max_delay_cs(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int rc, max_delay_cs;
|
||||||
|
struct ctl_table dummy = *table;
|
||||||
|
long d;
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
dummy.data = &max_delay_cs;
|
||||||
|
dummy.proc_handler = &proc_dointvec;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
max_delay_cs = 0;
|
||||||
|
rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
|
||||||
|
if (rc < 0)
|
||||||
|
RETURN(rc);
|
||||||
|
|
||||||
|
if (max_delay_cs <= 0)
|
||||||
|
RETURN(-EINVAL);
|
||||||
|
|
||||||
|
d = (max_delay_cs * HZ) / 100;
|
||||||
|
if (d == 0 || d < spl_console_min_delay)
|
||||||
|
RETURN(-EINVAL);
|
||||||
|
|
||||||
|
spl_console_max_delay = d;
|
||||||
|
} else {
|
||||||
|
max_delay_cs = (spl_console_max_delay * 100) / HZ;
|
||||||
|
rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_console_min_delay_cs(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int rc, min_delay_cs;
|
||||||
|
struct ctl_table dummy = *table;
|
||||||
|
long d;
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
dummy.data = &min_delay_cs;
|
||||||
|
dummy.proc_handler = &proc_dointvec;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
min_delay_cs = 0;
|
||||||
|
rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
|
||||||
|
if (rc < 0)
|
||||||
|
RETURN(rc);
|
||||||
|
|
||||||
|
if (min_delay_cs <= 0)
|
||||||
|
RETURN(-EINVAL);
|
||||||
|
|
||||||
|
d = (min_delay_cs * HZ) / 100;
|
||||||
|
if (d == 0 || d > spl_console_max_delay)
|
||||||
|
RETURN(-EINVAL);
|
||||||
|
|
||||||
|
spl_console_min_delay = d;
|
||||||
|
} else {
|
||||||
|
min_delay_cs = (spl_console_min_delay * 100) / HZ;
|
||||||
|
rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_console_backoff(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int rc, backoff;
|
||||||
|
struct ctl_table dummy = *table;
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
dummy.data = &backoff;
|
||||||
|
dummy.proc_handler = &proc_dointvec;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
backoff = 0;
|
||||||
|
rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
|
||||||
|
if (rc < 0)
|
||||||
|
RETURN(rc);
|
||||||
|
|
||||||
|
if (backoff <= 0)
|
||||||
|
RETURN(-EINVAL);
|
||||||
|
|
||||||
|
spl_console_backoff = backoff;
|
||||||
|
} else {
|
||||||
|
backoff = spl_console_backoff;
|
||||||
|
rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_doatomic64(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
unsigned long min = 0, max = ~0, val;
|
||||||
|
struct ctl_table dummy = *table;
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
dummy.data = &val;
|
||||||
|
dummy.proc_handler = &proc_dointvec;
|
||||||
|
dummy.extra1 = &min;
|
||||||
|
dummy.extra2 = &max;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
*ppos += *lenp;
|
||||||
|
} else {
|
||||||
|
val = atomic_read((atomic64_t *)table->data);
|
||||||
|
rc = proc_doulongvec_minmax(&dummy, write, filp,
|
||||||
|
buffer, lenp, ppos);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proc_dohostid(struct ctl_table *table, int write, struct file *filp,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int len, rc = 0;
|
||||||
|
unsigned long val;
|
||||||
|
char *end, str[32];
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
/* We can't use proc_doulongvec_minmax() in the write
|
||||||
|
* case hear because hostid while a hex value has no
|
||||||
|
* leading 0x which confuses the helper function. */
|
||||||
|
rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
|
||||||
|
if (rc < 0)
|
||||||
|
RETURN(rc);
|
||||||
|
|
||||||
|
val = simple_strtoul(str, &end, 16);
|
||||||
|
if (str == end)
|
||||||
|
RETURN(-EINVAL);
|
||||||
|
|
||||||
|
spl_hostid = val;
|
||||||
|
sprintf(spl_hw_serial, "%lu", ((long)val >= 0) ? val : -val);
|
||||||
|
*ppos += *lenp;
|
||||||
|
} else {
|
||||||
|
len = snprintf(str, sizeof(str), "%lx", spl_hostid);
|
||||||
|
if (*ppos >= len)
|
||||||
|
rc = 0;
|
||||||
|
else
|
||||||
|
rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
|
||||||
|
|
||||||
|
if (rc >= 0) {
|
||||||
|
*lenp = rc;
|
||||||
|
*ppos += rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ctl_table spl_table[] = {
|
||||||
|
/* NB No .strategy entries have been provided since
|
||||||
|
* sysctl(8) prefers to go via /proc for portability.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_SUBSYS,
|
||||||
|
.procname = "debug_subsystem",
|
||||||
|
.data = &spl_debug_subsys,
|
||||||
|
.maxlen = sizeof(unsigned long),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dobitmasks
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_MASK,
|
||||||
|
.procname = "debug_mask",
|
||||||
|
.data = &spl_debug_mask,
|
||||||
|
.maxlen = sizeof(unsigned long),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dobitmasks
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_PRINTK,
|
||||||
|
.procname = "debug_printk",
|
||||||
|
.data = &spl_debug_printk,
|
||||||
|
.maxlen = sizeof(unsigned long),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dobitmasks
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_MB,
|
||||||
|
.procname = "debug_mb",
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_debug_mb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_BINARY,
|
||||||
|
.procname = "debug_binary",
|
||||||
|
.data = &spl_debug_binary,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dointvec,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_CATASTROPHE,
|
||||||
|
.procname = "catastrophe",
|
||||||
|
.data = &spl_debug_catastrophe,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = &proc_dointvec,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_PANIC_ON_BUG,
|
||||||
|
.procname = "panic_on_bug",
|
||||||
|
.data = &spl_debug_panic_on_bug,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dointvec
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_PATH,
|
||||||
|
.procname = "debug_path",
|
||||||
|
.data = spl_debug_file_path,
|
||||||
|
.maxlen = sizeof(spl_debug_file_path),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dostring,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_DEBUG_DUMP,
|
||||||
|
.procname = "debug_dump",
|
||||||
|
.mode = 0200,
|
||||||
|
.proc_handler = &proc_dump_kernel,
|
||||||
|
},
|
||||||
|
{ .ctl_name = CTL_DEBUG_FORCE_BUG,
|
||||||
|
.procname = "force_bug",
|
||||||
|
.mode = 0200,
|
||||||
|
.proc_handler = &proc_force_bug,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_CONSOLE_RATELIMIT,
|
||||||
|
.procname = "console_ratelimit",
|
||||||
|
.data = &spl_console_ratelimit,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dointvec,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_CONSOLE_MAX_DELAY_CS,
|
||||||
|
.procname = "console_max_delay_centisecs",
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_console_max_delay_cs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_CONSOLE_MIN_DELAY_CS,
|
||||||
|
.procname = "console_min_delay_centisecs",
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_console_min_delay_cs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_CONSOLE_BACKOFF,
|
||||||
|
.procname = "console_backoff",
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_console_backoff,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_STACK_SIZE,
|
||||||
|
.procname = "stack_max",
|
||||||
|
.data = &spl_debug_stack,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = &proc_dointvec,
|
||||||
|
},
|
||||||
|
#ifdef DEBUG_KMEM
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_KMEM_KMEMUSED,
|
||||||
|
.procname = "kmem_used",
|
||||||
|
.data = &kmem_alloc_used,
|
||||||
|
.maxlen = sizeof(atomic64_t),
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = &proc_doatomic64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_KMEM_KMEMMAX,
|
||||||
|
.procname = "kmem_max",
|
||||||
|
.data = &kmem_alloc_max,
|
||||||
|
.maxlen = sizeof(unsigned long),
|
||||||
|
.extra1 = &table_min,
|
||||||
|
.extra2 = &table_max,
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = &proc_doulongvec_minmax,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_KMEM_VMEMUSED,
|
||||||
|
.procname = "vmem_used",
|
||||||
|
.data = &vmem_alloc_used,
|
||||||
|
.maxlen = sizeof(atomic64_t),
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = &proc_doatomic64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_KMEM_VMEMMAX,
|
||||||
|
.procname = "vmem_max",
|
||||||
|
.data = &vmem_alloc_max,
|
||||||
|
.maxlen = sizeof(unsigned long),
|
||||||
|
.extra1 = &table_min,
|
||||||
|
.extra2 = &table_max,
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = &proc_doulongvec_minmax,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_HOSTID,
|
||||||
|
.procname = "hostid",
|
||||||
|
.data = &spl_hostid,
|
||||||
|
.maxlen = sizeof(unsigned long),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = &proc_dohostid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_HW_SERIAL,
|
||||||
|
.procname = "hw_serial",
|
||||||
|
.data = spl_hw_serial,
|
||||||
|
.maxlen = sizeof(spl_hw_serial),
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = &proc_dostring,
|
||||||
|
},
|
||||||
|
{ 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ctl_table spl_dir_table[] = {
|
||||||
|
{
|
||||||
|
.ctl_name = CTL_SPL,
|
||||||
|
.procname = "spl",
|
||||||
|
.mode = 0555,
|
||||||
|
.child = spl_table,
|
||||||
|
},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
proc_init(void)
|
||||||
|
{
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYSCTL
|
||||||
|
spl_header = register_sysctl_table(spl_dir_table, 0);
|
||||||
|
if (spl_header == NULL)
|
||||||
|
RETURN(-EUNATCH);
|
||||||
|
#endif
|
||||||
|
RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
proc_fini(void)
|
||||||
|
{
|
||||||
|
ENTRY;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYSCTL
|
||||||
|
ASSERT(spl_header != NULL);
|
||||||
|
unregister_sysctl_table(spl_header);
|
||||||
|
#endif
|
||||||
|
EXIT;
|
||||||
|
}
|
@ -588,7 +588,7 @@ vn_fini(void)
|
|||||||
|
|
||||||
rc = kmem_cache_destroy(vn_file_cache);
|
rc = kmem_cache_destroy(vn_file_cache);
|
||||||
if (rc)
|
if (rc)
|
||||||
printk("spl: Warning leaked vn_file_cache objects\n");
|
printk("spl: Warning leaked vn_file_cache objects, %d\n", rc);
|
||||||
|
|
||||||
vn_file_cache = NULL;
|
vn_file_cache = NULL;
|
||||||
spin_unlock(&vn_file_lock);
|
spin_unlock(&vn_file_lock);
|
||||||
@ -598,7 +598,7 @@ vn_fini(void)
|
|||||||
|
|
||||||
rc = kmem_cache_destroy(vn_cache);
|
rc = kmem_cache_destroy(vn_cache);
|
||||||
if (rc)
|
if (rc)
|
||||||
printk("spl: Warning leaked vn_cache objects\n");
|
printk("spl: Warning leaked vn_cache objects, %d\n", rc);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} /* vn_fini() */
|
} /* vn_fini() */
|
||||||
|
Loading…
Reference in New Issue
Block a user