From: liangzhen Date: Fri, 21 Sep 2007 14:48:35 +0000 (+0000) Subject: Branch HEAD X-Git-Tag: v1_7_0_51~704 X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=commitdiff_plain;h=180c2dd3915383bbfdcf6cad6a8058ca045c8b61 Branch HEAD Sysctl entries for /proc/sys/lnet (Patch by eeb) b=12782 i=Liang --- diff --git a/lnet/ChangeLog b/lnet/ChangeLog index fcc8312..39b2d31 100644 --- a/lnet/ChangeLog +++ b/lnet/ChangeLog @@ -28,6 +28,11 @@ tbd Cluster File Systems, Inc. mxlnd - MX 1.2.1 or later, ptllnd - Portals 3.3 / UNICOS/lc 1.5.x, 2.0.x +Severity : normal +Bugzilla : 12782 +Description: /proc/sys/lnet has non-sysctl entries +Details : Updating dump_kernel/daemon_file/debug_mb to use sysctl variables + Severity : major Bugzilla : 13236 Description: TOE Kernel panic by ksocklnd diff --git a/lnet/include/libcfs/linux/portals_compat25.h b/lnet/include/libcfs/linux/portals_compat25.h index 4caf8c8..e8152bc 100644 --- a/lnet/include/libcfs/linux/portals_compat25.h +++ b/lnet/include/libcfs/linux/portals_compat25.h @@ -110,6 +110,7 @@ typedef unsigned long cpumask_t; #define LL_PROC_PROTO(name) \ name(cfs_sysctl_table_t *table, int write, struct file *filp, \ void __user *buffer, size_t *lenp) +#define DECLARE_LL_PROC_PPOS_DECL loff_t *ppos = &filp->f_pos #else #define ll_proc_dointvec(table, write, filp, buffer, lenp, ppos) \ proc_dointvec(table, write, filp, buffer, lenp, ppos); @@ -118,6 +119,7 @@ typedef unsigned long cpumask_t; #define LL_PROC_PROTO(name) \ name(cfs_sysctl_table_t *table, int write, struct file *filp, \ void __user *buffer, size_t *lenp, loff_t *ppos) +#define DECLARE_LL_PROC_PPOS #endif #endif /* _PORTALS_COMPAT_H */ diff --git a/lnet/libcfs/darwin/darwin-proc.c b/lnet/libcfs/darwin/darwin-proc.c index a38902a..a001f5b 100644 --- a/lnet/libcfs/darwin/darwin-proc.c +++ b/lnet/libcfs/darwin/darwin-proc.c @@ -49,9 +49,86 @@ extern unsigned int libcfs_console_ratelimit; extern unsigned int libcfs_catastrophe; extern atomic_t libcfs_kmemory; -extern long max_debug_mb; -extern int cfs_trace_daemon SYSCTL_HANDLER_ARGS; -extern int cfs_debug_mb SYSCTL_HANDLER_ARGS; +static int sysctl_debug_kernel SYSCTL_HANDLER_ARGS +{ +#error "Check me" + const int maxstr = 1024; + char *str; + int error; + + if (req->newptr == USER_ADDR_NULL) { + /* read request */ + return -EINVAL; + } + + /* write request */ + error = trace_allocate_string_buffer(&str, maxstr + 1); + if (error != 0) + return error; + + error = SYSCTL_IN(req, str, maxstr); + + /* NB str guaranteed terminted */ + if (error == 0) + error = tracefile_dump_all_pages(str); + + trace_free_string_buffer(str, maxstr + 1); + return error; +} + +static int sysctl_daemon_file SYSCTL_HANDLER_ARGS +{ +#error "Check me" + int error; + char *str; + + if (req->newptr == USER_ADDR_NULL) { + /* a read */ + tracefile_read_lock(); + + /* include terminating '\0' */ + error = SYSCTL_OUT(req, tracefile, strlen(tracefile) + 1); + + tracefile_read_unlock(); + return error; + } + + /* write request */ + error = trace_allocate_string_buffer(&str, TRACEFILE_NAME_SIZE); + if (error != 0) + return error; + + error = SYSCTL_IN(req, str, TRACEFILE_NAME_SIZE - 1); + + /* NB str guaranteed terminted */ + if (error == 0) + error = trace_daemon_command(str); + + trace_free_string_buffer(str, TRACEFILE_NAME_SIZE); + return error; +} + + +static int sysctl_debug_mb SYSCTL_HANDLER_ARGS +{ +#error "Check me" + long mb; + int error; + + if (req->newptr == USER_ADDR_NULL) { + /* read */ + mb = trace_get_debug_mb(); + error = SYSCTL_OUT(req, &mb, sizeof(mb)); + } else { + /* write */ + error = SYSCTL_IN(req, &mb, sizeof(mb)); + if (error == 0) + error = trace_set_debug_mb(mb); + } + + return error; +} + /* * sysctl table for lnet */ @@ -80,12 +157,17 @@ SYSCTL_INT(_lnet, OID_AUTO, memused, SYSCTL_INT(_lnet, OID_AUTO, catastrophe, CTLTYPE_INT | CTLFLAG_RW, (int *)&libcfs_catastrophe, 0, "catastrophe"); -SYSCTL_PROC(_lnet, OID_AUTO, trace_daemon, + +#error "check me" +SYSCTL_PROC(_lnet, OID_AUTO, debug_kernel, + CTLTYPE_STRING | CTLFLAG_W, 0, + 0, &sysctl_debug_kernel, "A", "debug_kernel"); +SYSCTL_PROC(_lnet, OID_AUTO, daemon_file, CTLTYPE_STRING | CTLFLAG_RW, 0, - 0, &cfs_trace_daemon, "A", "trace daemon"); + 0, &sysctl_daemon_file, "A", "daemon_file"); SYSCTL_PROC(_lnet, OID_AUTO, debug_mb, - CTLTYPE_INT | CTLFLAG_RW, &max_debug_mb, - 0, &cfs_debug_mb, "L", "max debug size"); + CTLTYPE_INT | CTLFLAG_RW, 0, + 0, &sysctl_debug_mb, "L", "debug_mb"); static cfs_sysctl_table_t top_table[] = { @@ -97,7 +179,8 @@ static cfs_sysctl_table_t top_table[] = { &sysctl__lnet_debug_path, &sysctl__lnet_memused, &sysctl__lnet_catastrophe, - &sysctl__lnet_trace_daemon, + &sysctl__lnet_debug_kernel, + &sysctl__lnet_daemon_file, &sysctl__lnet_debug_mb, NULL }; diff --git a/lnet/libcfs/darwin/darwin-tracefile.c b/lnet/libcfs/darwin/darwin-tracefile.c index bb1dc72..29dece3 100644 --- a/lnet/libcfs/darwin/darwin-tracefile.c +++ b/lnet/libcfs/darwin/darwin-tracefile.c @@ -12,12 +12,7 @@ #define M_TCD_MAX_PAGES (128 * 1280) extern union trace_data_union trace_data[NR_CPUS]; -extern char *tracefile; -extern long long tracefile_size; -extern int trace_start_thread(void); -extern void trace_stop_thread(void); -long max_debug_mb = M_TCD_MAX_PAGES; static long max_permit_mb = (64 * 1024); spinlock_t trace_cpu_serializer; @@ -185,95 +180,9 @@ void print_to_console(struct ptldebug_header *hdr, int mask, const char *buf, } } -/* - * Sysctl handle of libcfs - */ -#define MAX_TRACEFILE_PATH_LEN 256 -int cfs_trace_daemon SYSCTL_HANDLER_ARGS -{ - int error = 0; - char *name = NULL; - - if (req->newptr == USER_ADDR_NULL) { - /* a read */ - if (tracefile) - error = sysctl_handle_string(oidp, tracefile, 0, req); - else - error = sysctl_handle_string(oidp, "NA", 0, req); - - return error; - } - - /* now hanle write requests */ - MALLOC(name, char *, MAX_TRACEFILE_PATH_LEN + 1, M_TEMP, M_WAITOK | M_ZERO); - if (name == NULL) - return -ENOMEM; - name[0] = '\0'; - tracefile_write_lock(); - error = sysctl_handle_string(oidp, name, MAX_TRACEFILE_PATH_LEN + 1, req); - if (!error) { - if (strcmp(name, "stop") == 0) { - /* stop tracefile daemon */ - tracefile = NULL; - trace_stop_thread(); - goto out; - }else if (strncmp(name, "size=", 5) == 0) { - tracefile_size = simple_strtoul(name + 5, NULL, 0); - if (tracefile_size < 10 || tracefile_size > 20480) - tracefile_size = TRACEFILE_SIZE; - else - tracefile_size <<= 20; - goto out; - - } - if (name[0] != '/') { - error = -EINVAL; - goto out; - } - if (tracefile != NULL) - cfs_free(tracefile); - tracefile = name; - name = NULL; - trace_start_thread(); - } else { - /* Something was wrong with the write request */ - printf("sysctl debug daemon failed: %d.\n", error); - goto out; - } -out: - if (name != NULL) - FREE(name, M_TEMP); - tracefile_write_unlock(); - return error; -} -#undef MAX_TRACEFILE_PATH_LEN - - -int cfs_debug_mb SYSCTL_HANDLER_ARGS +int trace_max_debug_mb(void) { - int i; - int error = 0; - - error = sysctl_handle_long(oidp, oidp->oid_arg1, oidp->oid_arg2, req); - if (!error && req->newptr != USER_ADDR_NULL) { - /* We have a new value stored in the standard location */ - if (max_debug_mb <= 0) - return -EINVAL; - if (max_debug_mb > max_permit_mb) { - printf("sysctl debug_mb is too big: %d.\n", max_debug_mb); - return 0; - } - for (i = 0; i < NR_CPUS; i++) { - struct trace_cpu_data *tcd; - tcd = &trace_data[i].tcd; - tcd->tcd_max_pages = max_debug_mb; - } - } else if (req->newptr != USER_ADDR_NULL) { - /* Something was wrong with the write request */ - printf ("sysctl debug_mb fault: %d.\n", error); - } - - return error; + return max_permit_mb; } void diff --git a/lnet/libcfs/debug.c b/lnet/libcfs/debug.c index 1af9c13..58ef681 100644 --- a/lnet/libcfs/debug.c +++ b/lnet/libcfs/debug.c @@ -296,17 +296,31 @@ int libcfs_debug_str2mask(int *mask, const char *str, int is_subsys) { int m = 0; - int matched = 0; char op = 0; + int matched; int n; int t; + /* Allow a number for backwards compatibility */ + + for (n = strlen(str); n > 0; n--) + if (!isspace(str[n-1])) + break; + matched = n; + + if ((t = sscanf(str, "%i%n", &m, &matched)) >= 1 && + matched == n) { + *mask = m; + return 0; + } + /* must be a list of debug tokens or numbers separated by * whitespace and optionally an operator ('+' or '-'). If an operator * appears first in , '*mask' is used as the starting point * (relative), otherwise 0 is used (absolute). An operator applies to * all following tokens up to the next operator. */ + matched = 0; while (*str != 0) { while (isspace(*str)) /* skip whitespace */ str++; diff --git a/lnet/libcfs/linux/linux-proc.c b/lnet/libcfs/linux/linux-proc.c index 987eed7..55b6809 100644 --- a/lnet/libcfs/linux/linux-proc.c +++ b/lnet/libcfs/linux/linux-proc.c @@ -74,11 +74,132 @@ enum { PSDEV_LNET_MEMUSED, /* bytes currently PORTAL_ALLOCated */ PSDEV_LNET_CATASTROPHE, /* if we have LBUGged or panic'd */ PSDEV_LNET_PANIC_ON_LBUG, /* flag to panic on LBUG */ + PSDEV_LNET_DUMP_KERNEL, /* snapshot kernel debug buffer to file */ + PSDEV_LNET_DAEMON_FILE, /* spool kernel debug buffer to file */ + PSDEV_LNET_DEBUG_MB, /* size of debug buffer */ }; -int LL_PROC_PROTO(proc_dobitmasks); +static int +proc_call_handler(void *data, int write, + loff_t *ppos, void *buffer, size_t *lenp, + int (*handler)(void *data, int write, + loff_t pos, void *buffer, int len)) +{ + int rc = handler(data, write, *ppos, buffer, *lenp); + + if (rc < 0) + return rc; + + if (write) { + *ppos += *lenp; + } else { + *lenp = rc; + *ppos += rc; + } + return 0; +} + +#define DECLARE_PROC_HANDLER(name) \ +static int \ +LL_PROC_PROTO(name) \ +{ \ + DECLARE_LL_PROC_PPOS; \ + \ + return proc_call_handler(table->data, write, \ + ppos, buffer, lenp, \ + __##name); \ +} + +static int __proc_dobitmasks(void *data, int write, + loff_t pos, void *buffer, int nob) +{ + const int tmpstrlen = 512; + char *tmpstr; + int rc; + unsigned int *mask = data; + int is_subsys = (mask == &libcfs_subsystem_debug) ? 1 : 0; + + rc = trace_allocate_string_buffer(&tmpstr, tmpstrlen); + if (rc < 0) + return rc; + + if (!write) { + libcfs_debug_mask2str(tmpstr, tmpstrlen, *mask, is_subsys); + rc = strlen(tmpstr); + + if (pos >= rc) { + rc = 0; + } else { + rc = trace_copyout_string(buffer, nob, + tmpstr + pos, "\n"); + } + } else { + rc = trace_copyin_string(tmpstr, tmpstrlen, buffer, nob); + if (rc < 0) + return rc; + + rc = libcfs_debug_str2mask(mask, tmpstr, is_subsys); + } + + trace_free_string_buffer(tmpstr, tmpstrlen); + return rc; +} + +DECLARE_PROC_HANDLER(proc_dobitmasks) + +static int __proc_dump_kernel(void *data, int write, + loff_t pos, void *buffer, int nob) +{ + if (!write) + return 0; + + return trace_dump_debug_buffer_usrstr(buffer, nob); +} + +DECLARE_PROC_HANDLER(proc_dump_kernel) + +static int __proc_daemon_file(void *data, int write, + loff_t pos, void *buffer, int nob) +{ + if (!write) { + int len = strlen(tracefile); + + if (pos >= len) + return 0; + + return trace_copyout_string(buffer, nob, + tracefile + pos, "\n"); + } + + return trace_daemon_command_usrstr(buffer, nob); +} + +DECLARE_PROC_HANDLER(proc_daemon_file) + +static int __proc_debug_mb(void *data, int write, + loff_t pos, void *buffer, int nob) +{ + if (!write) { + char tmpstr[32]; + int len = snprintf(tmpstr, sizeof(tmpstr), "%d", + trace_get_debug_mb()); + + if (pos >= len) + return 0; + + return trace_copyout_string(buffer, nob, tmpstr + pos, "\n"); + } + + return trace_set_debug_mb_usrstr(buffer, nob); +} + +DECLARE_PROC_HANDLER(proc_debug_mb) static cfs_sysctl_table_t lnet_table[] = { + /* + * NB No .strategy entries have been provided since sysctl(8) prefers + * to go via /proc for portability. + */ { .ctl_name = PSDEV_DEBUG, .procname = "debug", @@ -119,7 +240,6 @@ static cfs_sysctl_table_t lnet_table[] = { .maxlen = sizeof(debug_file_path), .mode = 0644, .proc_handler = &proc_dostring, - .strategy = &sysctl_string }, { @@ -129,7 +249,6 @@ static cfs_sysctl_table_t lnet_table[] = { .maxlen = sizeof(lnet_upcall), .mode = 0644, .proc_handler = &proc_dostring, - .strategy = &sysctl_string }, { .ctl_name = PSDEV_LNET_MEMUSED, @@ -155,6 +274,24 @@ static cfs_sysctl_table_t lnet_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, + { + .ctl_name = PSDEV_LNET_DUMP_KERNEL, + .procname = "dump_kernel", + .mode = 0200, + .proc_handler = &proc_dump_kernel, + }, + { + .ctl_name = PSDEV_LNET_DAEMON_FILE, + .procname = "daemon_file", + .mode = 0644, + .proc_handler = &proc_daemon_file, + }, + { + .ctl_name = PSDEV_LNET_DEBUG_MB, + .procname = "debug_mb", + .mode = 0644, + .proc_handler = &proc_debug_mb, + }, {0} }; @@ -170,115 +307,21 @@ static cfs_sysctl_table_t top_table[2] = { {0} }; -int LL_PROC_PROTO(proc_dobitmasks) -{ - const int tmpstrlen = 512; - char *str; - int rc = 0; - /* the proc filling api stumps me always, coax proc_dointvec - * and proc_dostring into doing the drudgery by cheating - * with a dummy ctl_table - */ - cfs_sysctl_table_t dummy = *table; - unsigned int *mask = (unsigned int *)table->data; - int is_subsys = (mask == &libcfs_subsystem_debug) ? 1 : 0; - - str = kmalloc(tmpstrlen, GFP_USER); - if (str == NULL) - return -ENOMEM; - - if (write) { - size_t oldlen = *lenp; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8) - loff_t oldpos = *ppos; -#endif - - dummy.proc_handler = &proc_dointvec; - - /* old proc interface allows user to specify just an int - * value; be compatible and don't break userland. - */ - rc = ll_proc_dointvec(&dummy, write, filp, buffer, lenp, ppos); - - if (rc != -EINVAL) - goto out; - - /* using new interface */ - dummy.data = str; - dummy.maxlen = tmpstrlen; - dummy.proc_handler = &proc_dostring; - - /* proc_dointvec might have changed these */ - *lenp = oldlen; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8) - *ppos = oldpos; -#endif - - rc = ll_proc_dostring(&dummy, write, filp, buffer, lenp, ppos); - - if (rc != 0) - goto out; - - rc = libcfs_debug_str2mask(mask, dummy.data, is_subsys); - } else { - dummy.data = str; - dummy.maxlen = tmpstrlen; - dummy.proc_handler = &proc_dostring; - - libcfs_debug_mask2str(dummy.data, dummy.maxlen,*mask,is_subsys); - - rc = ll_proc_dostring(&dummy, write, filp, buffer, lenp, ppos); - } - -out: - kfree(str); - return rc; -} - int insert_proc(void) { - struct proc_dir_entry *ent; - #ifdef CONFIG_SYSCTL - if (!lnet_table_header) + if (lnet_table_header == NULL) lnet_table_header = cfs_register_sysctl_table(top_table, 0); #endif - - ent = create_proc_entry("sys/lnet/dump_kernel", 0, NULL); - if (ent == NULL) { - CERROR("couldn't register dump_kernel\n"); - return -1; - } - ent->write_proc = trace_dk; - - ent = create_proc_entry("sys/lnet/daemon_file", 0, NULL); - if (ent == NULL) { - CERROR("couldn't register daemon_file\n"); - return -1; - } - ent->write_proc = trace_write_daemon_file; - ent->read_proc = trace_read_daemon_file; - - ent = create_proc_entry("sys/lnet/debug_mb", 0, NULL); - if (ent == NULL) { - CERROR("couldn't register debug_mb\n"); - return -1; - } - ent->write_proc = trace_write_debug_mb; - ent->read_proc = trace_read_debug_mb; - return 0; } void remove_proc(void) { - remove_proc_entry("sys/lnet/dump_kernel", NULL); - remove_proc_entry("sys/lnet/daemon_file", NULL); - remove_proc_entry("sys/lnet/debug_mb", NULL); - #ifdef CONFIG_SYSCTL - if (lnet_table_header) + if (lnet_table_header != NULL) cfs_unregister_sysctl_table(lnet_table_header); + lnet_table_header = NULL; #endif } diff --git a/lnet/libcfs/linux/linux-tracefile.c b/lnet/libcfs/linux/linux-tracefile.c index 1fb38cf..a3a3aad 100644 --- a/lnet/libcfs/linux/linux-tracefile.c +++ b/lnet/libcfs/linux/linux-tracefile.c @@ -11,8 +11,6 @@ #endif extern union trace_data_union trace_data[NR_CPUS]; -extern char *tracefile; -extern long long tracefile_size; char *trace_console_buffers[NR_CPUS][3]; @@ -181,126 +179,11 @@ void print_to_console(struct ptldebug_header *hdr, int mask, const char *buf, return; } -int trace_write_daemon_file(struct file *file, const char *buffer, - unsigned long count, void *data) +int trace_max_debug_mb(void) { - char *name; - unsigned long off; - int rc; - - name = kmalloc(count + 1, GFP_KERNEL); - if (name == NULL) - return -ENOMEM; - - if (copy_from_user(name, buffer, count)) { - rc = -EFAULT; - goto out; - } - - /* be nice and strip out trailing '\n' */ - for (off = count ; off > 2 && isspace(name[off - 1]); off--) - ; - - name[off] = '\0'; - - tracefile_write_lock(); - if (strcmp(name, "stop") == 0) { - tracefile = NULL; - trace_stop_thread(); - goto out_sem; - } else if (strncmp(name, "size=", 5) == 0) { - tracefile_size = simple_strtoul(name + 5, NULL, 0); - if (tracefile_size < 10 || tracefile_size > 20480) - tracefile_size = TRACEFILE_SIZE; - else - tracefile_size <<= 20; - goto out_sem; - } - - if (name[0] != '/') { - rc = -EINVAL; - goto out_sem; - } - - if (tracefile != NULL) - kfree(tracefile); - - tracefile = name; - name = NULL; - printk(KERN_INFO "Lustre: debug daemon will attempt to start writing " - "to %s (%lukB max)\n", tracefile, (long)(tracefile_size >> 10)); - - trace_start_thread(); -out_sem: - tracefile_write_unlock(); -out: - kfree(name); - return count; -} - -int trace_read_daemon_file(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int rc; - - tracefile_read_lock(); - rc = snprintf(page, count, "%s", tracefile); - tracefile_read_unlock(); - - return rc; -} - -int trace_write_debug_mb(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - char string[32]; - int i; - unsigned max; - - if (count >= sizeof(string)) { - printk(KERN_ERR "Lustre: value too large (length %lu bytes)\n", - count); - return -EOVERFLOW; - } - - if (copy_from_user(string, buffer, count)) - return -EFAULT; - - max = simple_strtoul(string, NULL, 0); - if (max == 0) - return -EINVAL; - - if (max > (num_physpages >> (20 - 2 - CFS_PAGE_SHIFT)) / 5 || max >= 512) { - printk(KERN_ERR "Lustre: Refusing to set debug buffer size to " - "%dMB, which is more than 80%% of available RAM (%lu)\n", - max, (num_physpages >> (20 - 2 - CFS_PAGE_SHIFT)) / 5); - return -EINVAL; - } - - max /= smp_num_cpus; - - for (i = 0; i < NR_CPUS; i++) { - struct trace_cpu_data *tcd; - tcd = &trace_data[i].tcd; - tcd->tcd_max_pages = max << (20 - CFS_PAGE_SHIFT); - } - return count; -} - -int trace_read_debug_mb(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct trace_cpu_data *tcd; - int rc; - - tcd = trace_get_tcd(); - __LASSERT (tcd != NULL); - - rc = snprintf(page, count, "%lu\n", - (tcd->tcd_max_pages >> (20 - CFS_PAGE_SHIFT)) * smp_num_cpus); - - trace_put_tcd(tcd); - return rc; + int total_mb = (num_physpages >> (20 - CFS_PAGE_SHIFT)); + + return MAX(512, (total_mb * 80)/100); } void diff --git a/lnet/libcfs/tracefile.c b/lnet/libcfs/tracefile.c index 4bf6693..45b3640 100644 --- a/lnet/libcfs/tracefile.c +++ b/lnet/libcfs/tracefile.c @@ -32,7 +32,7 @@ /* XXX move things up to the top, comment */ union trace_data_union trace_data[NR_CPUS] __cacheline_aligned; -char *tracefile = NULL; +char tracefile[TRACEFILE_NAME_SIZE]; int64_t tracefile_size = TRACEFILE_SIZE; static struct tracefiled_ctl trace_tctl; struct semaphore trace_thread_sem; @@ -718,41 +718,213 @@ void trace_flush_pages(void) } } -int trace_dk(struct file *file, const char *buffer, unsigned long count, - void *data) +int trace_copyin_string(char *knl_buffer, int knl_buffer_nob, + const char *usr_buffer, int usr_buffer_nob) { - char *name; - unsigned long off; - int rc; + int nob; + + if (usr_buffer_nob > knl_buffer_nob) + return -EOVERFLOW; + + if (copy_from_user((void *)knl_buffer, + (void *)usr_buffer, usr_buffer_nob)) + return -EFAULT; + + nob = strnlen(knl_buffer, usr_buffer_nob); + while (nob-- >= 0) /* strip trailing whitespace */ + if (!isspace(knl_buffer[nob])) + break; + + if (nob < 0) /* empty string */ + return -EINVAL; + + if (nob == knl_buffer_nob) /* no space to terminate */ + return -EOVERFLOW; + + knl_buffer[nob + 1] = 0; /* terminate */ + return 0; +} + +int trace_copyout_string(char *usr_buffer, int usr_buffer_nob, + const char *knl_buffer, 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 nob = strlen(knl_buffer); + + if (nob > usr_buffer_nob) + nob = usr_buffer_nob; + + if (copy_to_user(usr_buffer, knl_buffer, nob)) + return -EFAULT; + + if (append != NULL && nob < usr_buffer_nob) { + if (copy_to_user(usr_buffer + nob, append, 1)) + return -EFAULT; + + nob++; + } + + return nob; +} - name = cfs_alloc(count + 1, CFS_ALLOC_STD); - if (name == NULL) +int trace_allocate_string_buffer(char **str, int nob) +{ + if (nob > 2 * CFS_PAGE_SIZE) /* string must be "sensible" */ + return -EINVAL; + + *str = cfs_alloc(nob, CFS_ALLOC_STD | CFS_ALLOC_ZERO); + if (*str == NULL) return -ENOMEM; - if (copy_from_user((void *)name, (void *)buffer, count)) { - rc = -EFAULT; + return 0; +} + +void trace_free_string_buffer(char *str, int nob) +{ + cfs_free(str); +} + +int trace_dump_debug_buffer_usrstr(void *usr_str, int usr_str_nob) +{ + char *str; + int rc; + + rc = trace_allocate_string_buffer(&str, usr_str_nob + 1); + if (rc != 0) + return rc; + + rc = trace_copyin_string(str, usr_str_nob + 1, + usr_str, usr_str_nob); + if (rc != 0) goto out; - } #if !defined(__WINNT__) - if (name[0] != '/') { + if (str[0] != '/') { rc = -EINVAL; goto out; } #endif + rc = tracefile_dump_all_pages(str); +out: + trace_free_string_buffer(str, usr_str_nob + 1); + return rc; +} + +int trace_daemon_command(char *str) +{ + int rc = 0; + + tracefile_write_lock(); + + if (strcmp(str, "stop") == 0) { + trace_stop_thread(); + memset(tracefile, 0, sizeof(tracefile)); + + } else if (strncmp(str, "size=", 5) == 0) { + tracefile_size = simple_strtoul(str + 5, NULL, 0); + if (tracefile_size < 10 || tracefile_size > 20480) + tracefile_size = TRACEFILE_SIZE; + else + tracefile_size <<= 20; + + } else if (strlen(str) >= sizeof(tracefile)) { + rc = -ENAMETOOLONG; +#ifndef __WINNT__ + } else if (str[0] != '/') { + rc = -EINVAL; +#endif + } else { + strcpy(tracefile, str); - /* be nice and strip out trailing '\n' */ - for (off = count ; off > 2 && isspace(name[off - 1]); off--) - ; + printk(KERN_INFO "Lustre: debug daemon will attempt to start writing " + "to %s (%lukB max)\n", tracefile, + (long)(tracefile_size >> 10)); - name[off] = '\0'; - rc = tracefile_dump_all_pages(name); -out: - if (name) - cfs_free(name); - return count; + trace_start_thread(); + } + + tracefile_write_unlock(); + return rc; +} + +int trace_daemon_command_usrstr(void *usr_str, int usr_str_nob) +{ + char *str; + int rc; + + rc = trace_allocate_string_buffer(&str, usr_str_nob + 1); + if (rc != 0) + return rc; + + rc = trace_copyin_string(str, usr_str_nob + 1, + usr_str, usr_str_nob); + if (rc == 0) + rc = trace_daemon_command(str); + + trace_free_string_buffer(str, usr_str_nob + 1); + return rc; +} + +int trace_set_debug_mb(int mb) +{ + int i; + int limit = trace_max_debug_mb(); + + if (mb <= 0) + return -EINVAL; + + if (mb > limit) { + printk(KERN_ERR "Lustre: Refusing to set debug buffer size to " + "%dMB - limit is %d\n", mb, limit); + return -EINVAL; + } + + mb /= smp_num_cpus; + + tracefile_write_lock(); + + for (i = 0; i < NR_CPUS; i++) { + struct trace_cpu_data *tcd = &trace_data[i].tcd; + + tcd->tcd_max_pages = mb << (20 - CFS_PAGE_SHIFT); + } + + tracefile_write_unlock(); + + return 0; +} + +int trace_set_debug_mb_usrstr(void *usr_str, int usr_str_nob) +{ + char str[32]; + int rc; + + rc = trace_copyin_string(str, sizeof(str), usr_str, usr_str_nob); + if (rc < 0) + return rc; + + return trace_set_debug_mb(simple_strtoul(str, NULL, 0)); +} + +int trace_get_debug_mb(void) +{ + int i; + int total_pages = 0; + + tracefile_read_lock(); + + for (i = 0; i < NR_CPUS; i++) { + struct trace_cpu_data *tcd = &trace_data[i].tcd; + + total_pages += tcd->tcd_max_pages; + } + + tracefile_read_unlock(); + + return total_pages >> (20 - CFS_PAGE_SHIFT); } -EXPORT_SYMBOL(trace_dk); static int tracefiled(void *arg) { @@ -793,12 +965,13 @@ static int tracefiled(void *arg) filp = NULL; tracefile_read_lock(); - if (tracefile != NULL) { + if (tracefile[0] != 0) { filp = cfs_filp_open(tracefile, O_CREAT | O_RDWR | O_LARGEFILE, 0600, &rc); if (!(filp)) - printk("couldn't open %s: %d\n", tracefile, rc); + printk(KERN_WARNING "couldn't open %s: %d\n", + tracefile, rc); } tracefile_read_unlock(); if (filp == NULL) { @@ -877,7 +1050,7 @@ void trace_stop_thread(void) mutex_down(&trace_thread_sem); if (thread_running) { - printk(KERN_INFO "Shutting down debug daemon thread...\n"); + printk(KERN_INFO "Lustre: shutting down debug daemon thread...\n"); atomic_set(&tctl->tctl_shutdown, 1); wait_for_completion(&tctl->tctl_stop); thread_running = 0; diff --git a/lnet/libcfs/tracefile.h b/lnet/libcfs/tracefile.h index f3568e9..8b09d6e 100644 --- a/lnet/libcfs/tracefile.h +++ b/lnet/libcfs/tracefile.h @@ -5,6 +5,10 @@ /* trace file lock routines */ +#define TRACEFILE_NAME_SIZE 1024 +extern char tracefile[TRACEFILE_NAME_SIZE]; +extern long long tracefile_size; + int tracefile_init_arch(void); void tracefile_fini_arch(void); @@ -20,16 +24,21 @@ int trace_start_thread(void); void trace_stop_thread(void); int tracefile_init(void); void tracefile_exit(void); -int trace_write_daemon_file(struct file *file, const char *buffer, - unsigned long count, void *data); -int trace_read_daemon_file(char *page, char **start, off_t off, int count, - int *eof, void *data); -int trace_write_debug_mb(struct file *file, const char *buffer, - unsigned long count, void *data); -int trace_read_debug_mb(char *page, char **start, off_t off, int count, - int *eof, void *data); -int trace_dk(struct file *file, const char *buffer, unsigned long count, - void *data); + + + +int trace_copyin_string(char *knl_buffer, int knl_buffer_nob, + const char *usr_buffer, int usr_buffer_nob); +int trace_copyout_string(char *usr_buffer, int usr_buffer_nob, + const char *knl_str, char *append); +int trace_allocate_string_buffer(char **str, int nob); +void trace_free_string_buffer(char *str, int nob); +int trace_dump_debug_buffer_usrstr(void *usr_str, int usr_str_nob); +int trace_daemon_command(char *str); +int trace_daemon_command_usrstr(void *usr_str, int usr_str_nob); +int trace_set_debug_mb(int mb); +int trace_set_debug_mb_usrstr(void *usr_str, int usr_str_nob); +int trace_get_debug_mb(void); extern void libcfs_debug_dumplog_internal(void *arg); extern void libcfs_register_panic_notifier(void); @@ -37,6 +46,7 @@ extern void libcfs_unregister_panic_notifier(void); extern int libcfs_panic_in_progress; #ifdef LUSTRE_TRACEFILE_PRIVATE + /* * Private declare for tracefile */ @@ -179,6 +189,8 @@ extern void trace_put_console_buffer(char *buffer); extern void trace_call_on_all_cpus(void (*fn)(void *arg), void *arg); +extern int trace_max_debug_mb(void); + int trace_refill_stock(struct trace_cpu_data *tcd, int gfp, struct list_head *stock); diff --git a/lnet/libcfs/winnt/winnt-proc.c b/lnet/libcfs/winnt/winnt-proc.c index ebce30d..cfb8d38 100644 --- a/lnet/libcfs/winnt/winnt-proc.c +++ b/lnet/libcfs/winnt/winnt-proc.c @@ -1449,6 +1449,55 @@ static struct ctl_table top_table[2] = { {0} }; + +int trace_write_dump_kernel(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int rc = trace_dump_debug_buffer_usrstr(buffer, count); + + return (rc < 0) ? rc : count; +} + +int trace_write_daemon_file(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int rc = trace_daemon_command_usrstr(buffer, count); + + return (rc < 0) ? rc : count; +} + +int trace_read_daemon_file(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int rc; + + tracefile_read_lock(); + + rc = trace_copyout_string(page, count, tracefile, "\n"); + + tracefile_read_unlock(); + + return rc; +} + +int trace_write_debug_mb(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int rc = trace_set_debug_mb_userstr(buffer, count); + + return (rc < 0) ? rc : count; +} + +int trace_read_debug_mb(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char str[32]; + + snprintf(str, sizeof(str), "%d\n", trace_get_debug_mb()); + + return trace_copyout_string(page, count, str, NULL); +} + int insert_proc(void) { cfs_proc_entry_t *ent; @@ -1458,7 +1507,7 @@ int insert_proc(void) CERROR(("couldn't register dump_kernel\n")); return -1; } - ent->write_proc = trace_dk; + ent->write_proc = trace_write_dump_kernel; ent = create_proc_entry("sys/lnet/daemon_file", 0, NULL); if (ent == NULL) { diff --git a/lnet/libcfs/winnt/winnt-tracefile.c b/lnet/libcfs/winnt/winnt-tracefile.c index d172bff..c8ec79c 100644 --- a/lnet/libcfs/winnt/winnt-tracefile.c +++ b/lnet/libcfs/winnt/winnt-tracefile.c @@ -29,8 +29,6 @@ #endif extern union trace_data_union trace_data[NR_CPUS]; -extern char *tracefile; -extern int64_t tracefile_size; event_t tracefile_event; @@ -175,121 +173,11 @@ int tcd_owns_tage(struct trace_cpu_data *tcd, struct trace_page *tage) return 1; } - -int trace_write_daemon_file(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - char *name; - unsigned long off; - int rc; - - name =cfs_alloc(count + 1, 0); - if (name == NULL) - return -ENOMEM; - - if (copy_from_user((void *)name, (void*)buffer, count)) { - rc = -EFAULT; - goto out; - } - - /* be nice and strip out trailing '\n' */ - for (off = count ; off > 2 && isspace(name[off - 1]); off--) - ; - - name[off] = '\0'; - - tracefile_write_lock(); - if (strcmp(name, "stop") == 0) { - tracefile = NULL; - trace_stop_thread(); - goto out_sem; - } else if (strncmp(name, "size=", 5) == 0) { - tracefile_size = simple_strtoul(name + 5, NULL, 0); - if (tracefile_size < 10 || tracefile_size > 20480) - tracefile_size = TRACEFILE_SIZE; - else - tracefile_size <<= 20; - goto out_sem; - } - - if (tracefile != NULL) - cfs_free(tracefile); - - tracefile = name; - name = NULL; - printk(KERN_INFO "Lustre: debug daemon will attempt to start writing " - "to %s (%lukB max)\n", tracefile, (long)(tracefile_size >> 10)); - - trace_start_thread(); -out_sem: - tracefile_write_unlock(); -out: - if (name != NULL) - cfs_free(name); - return count; -} - -int trace_read_daemon_file(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int rc; - - tracefile_read_lock(); - rc = snprintf(page, count, "%s", tracefile); - tracefile_read_unlock(); - - return rc; -} - -int trace_write_debug_mb(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - char string[32]; - int i; - unsigned max; - - if (count >= sizeof(string)) { - printk(KERN_ERR "Lustre: value too large (length %lu bytes)\n", - count); - return -EOVERFLOW; - } - - if (copy_from_user((void *)string, (void *)buffer, count)) - return -EFAULT; - - max = simple_strtoul(string, NULL, 0); - if (max == 0) - return -EINVAL; - - if (max > (num_physpages >> (20 - 2 - CFS_PAGE_SHIFT)) / 5 || max >= 512) { - printk(KERN_ERR "Lustre: Refusing to set debug buffer size to " - "%dMB, which is more than 80%% of available RAM (%lu)\n", - max, (num_physpages >> (20 - 2 - CFS_PAGE_SHIFT)) / 5); - return -EINVAL; - } - - max /= smp_num_cpus; - - for (i = 0; i < NR_CPUS; i++) { - struct trace_cpu_data *tcd; - tcd = &trace_data[i].tcd; - tcd->tcd_max_pages = max << (20 - CFS_PAGE_SHIFT); - } - return count; -} - -int trace_read_debug_mb(char *page, char **start, off_t off, int count, - int *eof, void *data) +int trace_max_debug_mb(void) { - struct trace_cpu_data *tcd; - int rc; - - tcd = trace_get_tcd(); - LASSERT (tcd != NULL); - rc = snprintf(page, count, "%lu\n", - (tcd->tcd_max_pages >> (20 - CFS_PAGE_SHIFT)) * smp_num_cpus); - trace_put_tcd(tcd); - return rc; + int total_mb = (num_physpages >> (20 - CFS_PAGE_SHIFT)); + + return MAX(512, (total_mb * 80)/100); } void