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
#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);
#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 */
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
*/
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[] = {
&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
};
#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;
}
}
-/*
- * 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
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;
+ }
+
/* <str> must be a list of debug tokens or numbers separated by
* whitespace and optionally an operator ('+' or '-'). If an operator
* appears first in <str>, '*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++;
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",
.maxlen = sizeof(debug_file_path),
.mode = 0644,
.proc_handler = &proc_dostring,
- .strategy = &sysctl_string
},
{
.maxlen = sizeof(lnet_upcall),
.mode = 0644,
.proc_handler = &proc_dostring,
- .strategy = &sysctl_string
},
{
.ctl_name = PSDEV_LNET_MEMUSED,
.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}
};
{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
}
#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];
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
/* 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;
}
}
-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)
{
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) {
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;
/* 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);
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);
extern int libcfs_panic_in_progress;
#ifdef LUSTRE_TRACEFILE_PRIVATE
+
/*
* Private declare for tracefile
*/
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);
{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;
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) {
#endif
extern union trace_data_union trace_data[NR_CPUS];
-extern char *tracefile;
-extern int64_t tracefile_size;
event_t tracefile_event;
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