* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, 2015, Intel Corporation.
+ * Copyright (c) 2012, 2017, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
* Lustre is a trademark of Sun Microsystems, Inc.
*/
+#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/sysctl.h>
-#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
#include <asm/div64.h>
#define DEBUG_SUBSYSTEM S_LNET
#include <lnet/lib-lnet.h>
#include "tracefile.h"
-#ifdef CONFIG_SYSCTL
-static struct ctl_table_header *lnet_table_header;
-#endif
+static struct dentry *lnet_debugfs_root;
-static DECLARE_RWSEM(ioctl_list_sem);
-static LIST_HEAD(ioctl_list);
-
-int libcfs_register_ioctl(struct libcfs_ioctl_handler *hand)
-{
- int rc = 0;
-
- down_write(&ioctl_list_sem);
- if (!list_empty(&hand->item))
- rc = -EBUSY;
- else
- list_add_tail(&hand->item, &ioctl_list);
- up_write(&ioctl_list_sem);
-
- return rc;
-}
-EXPORT_SYMBOL(libcfs_register_ioctl);
-
-int libcfs_deregister_ioctl(struct libcfs_ioctl_handler *hand)
-{
- int rc = 0;
-
- down_write(&ioctl_list_sem);
- if (list_empty(&hand->item))
- rc = -ENOENT;
- else
- list_del_init(&hand->item);
- up_write(&ioctl_list_sem);
-
- return rc;
-}
-EXPORT_SYMBOL(libcfs_deregister_ioctl);
+BLOCKING_NOTIFIER_HEAD(libcfs_ioctl_list);
+EXPORT_SYMBOL(libcfs_ioctl_list);
int libcfs_ioctl(unsigned long cmd, void __user *uparam)
{
libcfs_debug_mark_buffer(data->ioc_inlbuf1);
break;
- default: {
- struct libcfs_ioctl_handler *hand;
-
- err = -EINVAL;
- down_read(&ioctl_list_sem);
- list_for_each_entry(hand, &ioctl_list, item) {
- err = hand->handle_ioctl(cmd, hdr);
- if (err == -EINVAL)
- continue;
-
- if (err == 0) {
- if (copy_to_user(uparam, hdr, hdr->ioc_len))
- err = -EFAULT;
- }
- break;
- }
- up_read(&ioctl_list_sem);
- break; }
+ default:
+ err = blocking_notifier_call_chain(&libcfs_ioctl_list,
+ cmd, hdr);
+ if (!(err & NOTIFY_STOP_MASK))
+ /* No-one claimed the ioctl */
+ err = -EINVAL;
+ else
+ err = notifier_to_errno(err);
+ if (!err)
+ if (copy_to_user(uparam, hdr, hdr->ioc_len))
+ err = -EFAULT;
+ break;
}
out:
LIBCFS_FREE(hdr, hdr->ioc_len);
RETURN(err);
}
-int
-lprocfs_call_handler(void *data, int write, loff_t *ppos,
- void __user *buffer, size_t *lenp,
- int (*handler)(void *data, int write, loff_t pos,
- void __user *buffer, int len))
+int lprocfs_call_handler(void *data, int write, loff_t *ppos,
+ void __user *buffer, size_t *lenp,
+ int (*handler)(void *data, int write, loff_t pos,
+ void __user *buffer, int len))
{
int rc = handler(data, write, *ppos, buffer, *lenp);
return rc;
}
-static int
-proc_dobitmasks(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
+static int proc_dobitmasks(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return lprocfs_call_handler(table->data, write, ppos, buffer, lenp,
__proc_dobitmasks);
return cfs_trace_dump_debug_buffer_usrstr(buffer, nob);
}
-static int
-proc_dump_kernel(struct ctl_table *table, int write, void __user *buffer,
- size_t *lenp, loff_t *ppos)
+static int proc_dump_kernel(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return lprocfs_call_handler(table->data, write, ppos, buffer, lenp,
__proc_dump_kernel);
return cfs_trace_daemon_command_usrstr(buffer, nob);
}
-static int
-proc_daemon_file(struct ctl_table *table, int write, void __user *buffer,
- size_t *lenp, loff_t *ppos)
+static int proc_daemon_file(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return lprocfs_call_handler(table->data, write, ppos, buffer, lenp,
__proc_daemon_file);
}
-static int __proc_debug_mb(void *data, int write,
- loff_t pos, void __user *buffer, int nob)
-{
- if (!write) {
- char tmpstr[32];
- int len = snprintf(tmpstr, sizeof(tmpstr), "%d",
- cfs_trace_get_debug_mb());
-
- if (pos >= len)
- return 0;
-
- return cfs_trace_copyout_string(buffer, nob, tmpstr + pos,
- "\n");
- }
-
- return cfs_trace_set_debug_mb_usrstr(buffer, nob);
-}
-
-static int
-proc_debug_mb(struct ctl_table *table, int write, void __user *buffer,
- size_t *lenp, loff_t *ppos)
+static int libcfs_force_lbug(struct ctl_table *table, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
{
- return lprocfs_call_handler(table->data, write, ppos, buffer, lenp,
- __proc_debug_mb);
+ if (write)
+ LBUG();
+ return 0;
}
-static int
-proc_console_max_delay_cs(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
+static int proc_fail_loc(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
- int rc, max_delay_cs;
- struct ctl_table dummy = *table;
- cfs_duration_t d;
-
- dummy.data = &max_delay_cs;
- dummy.proc_handler = &proc_dointvec;
-
- if (!write) { /* read */
- max_delay_cs = cfs_duration_sec(libcfs_console_max_delay * 100);
- rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
- return rc;
- }
-
- /* write */
- max_delay_cs = 0;
- rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
- if (rc < 0)
- return rc;
- if (max_delay_cs <= 0)
- return -EINVAL;
-
- d = cfs_time_seconds(max_delay_cs) / 100;
- if (d == 0 || d < libcfs_console_min_delay)
- return -EINVAL;
- libcfs_console_max_delay = d;
+ int rc;
+ long old_fail_loc = cfs_fail_loc;
+ rc = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
+ if (old_fail_loc != cfs_fail_loc)
+ wake_up(&cfs_race_waitq);
return rc;
}
-static int
-proc_console_min_delay_cs(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
+static int __proc_cpt_table(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
{
- int rc, min_delay_cs;
- struct ctl_table dummy = *table;
- cfs_duration_t d;
-
- dummy.data = &min_delay_cs;
- dummy.proc_handler = &proc_dointvec;
-
- if (!write) { /* read */
- min_delay_cs = cfs_duration_sec(libcfs_console_min_delay * 100);
- rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
- return rc;
- }
-
- /* write */
- min_delay_cs = 0;
- rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
- if (rc < 0)
- return rc;
- if (min_delay_cs <= 0)
- return -EINVAL;
+ char *buf = NULL;
+ int len = 4096;
+ int rc = 0;
- d = cfs_time_seconds(min_delay_cs) / 100;
- if (d == 0 || d > libcfs_console_max_delay)
- return -EINVAL;
- libcfs_console_min_delay = d;
+ if (write)
+ return -EPERM;
- return rc;
-}
+ LASSERT(cfs_cpt_table != NULL);
-static int
-proc_console_backoff(struct ctl_table *table, int write, void __user *buffer,
- size_t *lenp, loff_t *ppos)
-{
- int rc, backoff;
- struct ctl_table dummy = *table;
+ while (1) {
+ LIBCFS_ALLOC(buf, len);
+ if (buf == NULL)
+ return -ENOMEM;
- dummy.data = &backoff;
- dummy.proc_handler = &proc_dointvec;
+ rc = cfs_cpt_table_print(cfs_cpt_table, buf, len);
+ if (rc >= 0)
+ break;
- if (!write) { /* read */
- backoff = libcfs_console_backoff;
- rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
- return rc;
+ if (rc == -EFBIG) {
+ LIBCFS_FREE(buf, len);
+ len <<= 1;
+ continue;
+ }
+ goto out;
}
- /* write */
- backoff = 0;
- rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
- if (rc < 0)
- return rc;
-
- if (backoff <= 0)
- return -EINVAL;
-
- libcfs_console_backoff = backoff;
+ if (pos >= rc) {
+ rc = 0;
+ goto out;
+ }
+ rc = cfs_trace_copyout_string(buffer, nob, buf + pos, NULL);
+out:
+ if (buf != NULL)
+ LIBCFS_FREE(buf, len);
return rc;
}
-static int
-libcfs_force_lbug(struct ctl_table *table, int write, void __user *buffer,
- size_t *lenp, loff_t *ppos)
-{
- if (write)
- LBUG();
- return 0;
-}
-
-static int
-proc_fail_loc(struct ctl_table *table, int write, void __user *buffer,
- size_t *lenp, loff_t *ppos)
+static int proc_cpt_table(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
- int rc;
- long old_fail_loc = cfs_fail_loc;
-
- rc = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
- if (old_fail_loc != cfs_fail_loc)
- wake_up(&cfs_race_waitq);
- return rc;
+ return lprocfs_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_cpt_table);
}
-static int __proc_cpt_table(void *data, int write,
- loff_t pos, void __user *buffer, int nob)
+static int __proc_cpt_distance(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
{
char *buf = NULL;
int len = 4096;
if (buf == NULL)
return -ENOMEM;
- rc = cfs_cpt_table_print(cfs_cpt_table, buf, len);
+ rc = cfs_cpt_distance_print(cfs_cpt_table, buf, len);
if (rc >= 0)
break;
}
rc = cfs_trace_copyout_string(buffer, nob, buf + pos, NULL);
-out:
+ out:
if (buf != NULL)
LIBCFS_FREE(buf, len);
return rc;
}
-static int
-proc_cpt_table(struct ctl_table *table, int write, void __user *buffer,
- size_t *lenp, loff_t *ppos)
+static int proc_cpt_distance(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return lprocfs_call_handler(table->data, write, ppos, buffer, lenp,
- __proc_cpt_table);
+ __proc_cpt_distance);
}
static struct ctl_table lnet_table[] = {
- /*
- * NB No .strategy entries have been provided since sysctl(8) prefers
- * to go via /proc for portability.
- */
{
INIT_CTL_NAME
.procname = "debug",
},
{
INIT_CTL_NAME
- .procname = "console_ratelimit",
- .data = &libcfs_console_ratelimit,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec
- },
- {
- INIT_CTL_NAME
- .procname = "console_max_delay_centisecs",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_console_max_delay_cs
- },
- {
- INIT_CTL_NAME
- .procname = "console_min_delay_centisecs",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_console_min_delay_cs
- },
- {
- INIT_CTL_NAME
- .procname = "console_backoff",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_console_backoff
- },
- {
- INIT_CTL_NAME
- .procname = "debug_path",
- .data = libcfs_debug_file_path_arr,
- .maxlen = sizeof(libcfs_debug_file_path_arr),
- .mode = 0644,
- .proc_handler = &proc_dostring,
- },
- {
- INIT_CTL_NAME
.procname = "cpu_partition_table",
.maxlen = 128,
.mode = 0444,
},
{
INIT_CTL_NAME
- .procname = "upcall",
- .data = lnet_upcall,
- .maxlen = sizeof(lnet_upcall),
- .mode = 0644,
- .proc_handler = &proc_dostring,
+ .procname = "cpu_partition_distance",
+ .maxlen = 128,
+ .mode = 0444,
+ .proc_handler = &proc_cpt_distance,
},
{
INIT_CTL_NAME
},
{
INIT_CTL_NAME
- .procname = "panic_on_lbug",
- .data = &libcfs_panic_on_lbug,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
- {
- INIT_CTL_NAME
.procname = "dump_kernel",
.maxlen = 256,
.mode = 0200,
},
{
INIT_CTL_NAME
- .procname = "debug_mb",
- .mode = 0644,
- .proc_handler = &proc_debug_mb,
- },
- {
- INIT_CTL_NAME
.procname = "watchdog_ratelimit",
.data = &libcfs_watchdog_ratelimit,
.maxlen = sizeof(int),
}
};
-#ifdef CONFIG_SYSCTL
-static struct ctl_table top_table[] = {
- {
- INIT_CTL_NAME
- .procname = "lnet",
- .mode = 0555,
- .data = NULL,
- .maxlen = 0,
- .child = lnet_table,
- },
- { 0 }
+static const struct lnet_debugfs_symlink_def lnet_debugfs_symlinks[] = {
+ { .name = "console_ratelimit",
+ .target = "../../../module/libcfs/parameters/libcfs_console_ratelimit" },
+ { .name = "debug_path",
+ .target = "../../../module/libcfs/parameters/libcfs_debug_file_path" },
+ { .name = "panic_on_lbug",
+ .target = "../../../module/libcfs/parameters/libcfs_panic_on_lbug" },
+ { .name = "console_backoff",
+ .target = "../../../module/libcfs/parameters/libcfs_console_backoff" },
+ { .name = "debug_mb",
+ .target = "../../../module/libcfs/parameters/libcfs_debug_mb" },
+ { .name = "console_min_delay_centisecs",
+ .target = "../../../module/libcfs/parameters/libcfs_console_min_delay" },
+ { .name = "console_max_delay_centisecs",
+ .target = "../../../module/libcfs/parameters/libcfs_console_max_delay" },
+ { .name = NULL },
};
-#endif
-static int insert_proc(void)
+static ssize_t lnet_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
{
-#ifdef CONFIG_SYSCTL
- if (lnet_table_header == NULL)
- lnet_table_header = register_sysctl_table(top_table);
-#endif
- return 0;
+ struct ctl_table *table = filp->private_data;
+ ssize_t rc = -EINVAL;
+
+ if (table) {
+ rc = table->proc_handler(table, 0, buf, &count, ppos);
+ if (!rc)
+ rc = count;
+ }
+
+ return rc;
+}
+
+static ssize_t lnet_debugfs_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ctl_table *table = filp->private_data;
+ ssize_t rc = -EINVAL;
+
+ if (table) {
+ rc = table->proc_handler(table, 1, (void __user *)buf, &count,
+ ppos);
+ if (!rc)
+ rc = count;
+ }
+
+ return rc;
+}
+
+static const struct file_operations lnet_debugfs_file_operations_rw = {
+ .open = simple_open,
+ .read = lnet_debugfs_read,
+ .write = lnet_debugfs_write,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations lnet_debugfs_file_operations_ro = {
+ .open = simple_open,
+ .read = lnet_debugfs_read,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations lnet_debugfs_file_operations_wo = {
+ .open = simple_open,
+ .write = lnet_debugfs_write,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations *lnet_debugfs_fops_select(umode_t mode)
+{
+ if (!(mode & S_IWUGO))
+ return &lnet_debugfs_file_operations_ro;
+
+ if (!(mode & S_IRUGO))
+ return &lnet_debugfs_file_operations_wo;
+
+ return &lnet_debugfs_file_operations_rw;
+}
+
+void lnet_insert_debugfs(struct ctl_table *table)
+{
+ if (!lnet_debugfs_root)
+ lnet_debugfs_root = debugfs_create_dir("lnet", NULL);
+
+ /* Even if we cannot create, just ignore it altogether) */
+ if (IS_ERR_OR_NULL(lnet_debugfs_root))
+ return;
+
+ /* We don't save the dentry returned in next two calls, because
+ * we don't call debugfs_remove() but rather remove_recursive()
+ */
+ for (; table && table->procname; table++)
+ debugfs_create_file(table->procname, table->mode,
+ lnet_debugfs_root, table,
+ lnet_debugfs_fops_select(table->mode));
+}
+EXPORT_SYMBOL_GPL(lnet_insert_debugfs);
+
+static void lnet_insert_debugfs_links(
+ const struct lnet_debugfs_symlink_def *symlinks)
+{
+ for (; symlinks && symlinks->name; symlinks++)
+ debugfs_create_symlink(symlinks->name, lnet_debugfs_root,
+ symlinks->target);
}
-static void remove_proc(void)
+void lnet_remove_debugfs(struct ctl_table *table)
{
-#ifdef CONFIG_SYSCTL
- if (lnet_table_header != NULL)
- unregister_sysctl_table(lnet_table_header);
+ for (; table && table->procname; table++) {
+ struct qstr dname = QSTR_INIT(table->procname,
+ strlen(table->procname));
+ struct dentry *dentry;
- lnet_table_header = NULL;
-#endif
+ dentry = d_hash_and_lookup(lnet_debugfs_root, &dname);
+ debugfs_remove(dentry);
+ }
}
+EXPORT_SYMBOL_GPL(lnet_remove_debugfs);
static int __init libcfs_init(void)
{
goto cleanup_deregister;
}
- /* max to 4 threads, should be enough for rehash */
- rc = min(cfs_cpt_weight(cfs_cpt_table, CFS_CPT_ANY), 4);
- rc = cfs_wi_sched_create("cfs_rh", cfs_cpt_table, CFS_CPT_ANY,
- rc, &cfs_sched_rehash);
- if (rc != 0) {
- CERROR("Startup workitem scheduler: error: %d\n", rc);
+ cfs_rehash_wq = alloc_workqueue("cfs_rh", WQ_SYSFS, 4);
+ if (!cfs_rehash_wq) {
+ rc = -ENOMEM;
+ CERROR("libcfs: failed to start rehash workqueue: rc = %d\n",
+ rc);
goto cleanup_deregister;
}
goto cleanup_wi;
}
-
- rc = insert_proc();
- if (rc) {
- CERROR("insert_proc: error %d\n", rc);
- goto cleanup_crypto;
- }
+ lnet_insert_debugfs(lnet_table);
+ if (!IS_ERR_OR_NULL(lnet_debugfs_root))
+ lnet_insert_debugfs_links(lnet_debugfs_symlinks);
CDEBUG (D_OTHER, "portals setup OK\n");
return 0;
-cleanup_crypto:
- cfs_crypto_unregister();
cleanup_wi:
cfs_wi_shutdown();
cleanup_deregister:
{
int rc;
- remove_proc();
+ /* Remove everthing */
+ debugfs_remove_recursive(lnet_debugfs_root);
+ lnet_debugfs_root = NULL;
CDEBUG(D_MALLOC, "before Portals cleanup: kmem %d\n",
atomic_read(&libcfs_kmemory));
- if (cfs_sched_rehash != NULL) {
- cfs_wi_sched_destroy(cfs_sched_rehash);
- cfs_sched_rehash = NULL;
+ if (cfs_rehash_wq) {
+ destroy_workqueue(cfs_rehash_wq);
+ cfs_rehash_wq = NULL;
}
cfs_crypto_unregister();