@@ -33,10 +33,9 @@
#ifndef __LIBCFS_LIBCFS_H__
#define __LIBCFS_LIBCFS_H__
-#include <linux/notifier.h>
#include <linux/workqueue.h>
-#include <linux/sysctl.h>
+#include <uapi/linux/lnet/libcfs_ioctl.h>
#include <linux/libcfs/libcfs_debug.h>
#include <linux/libcfs/libcfs_private.h>
#include <linux/libcfs/libcfs_fail.h>
@@ -45,15 +44,8 @@
typedef s32 timeout_t;
-extern struct blocking_notifier_head libcfs_ioctl_list;
-static inline int notifier_from_ioctl_errno(int err)
-{
- if (err == -EINVAL)
- return NOTIFY_OK;
- return notifier_from_errno(err) | NOTIFY_STOP_MASK;
-}
-
int libcfs_setup(void);
+int libcfs_ioctl(unsigned int cmd, struct libcfs_ioctl_data *data);
extern struct workqueue_struct *cfs_rehash_wq;
@@ -1225,4 +1225,13 @@ lnet_set_route_aliveness(struct lnet_route *route, bool alive)
alive ? "up" : "down");
}
void lnet_update_ping_buffer(void);
+
+extern struct blocking_notifier_head lnet_ioctl_list;
+static inline int notifier_from_ioctl_errno(int err)
+{
+ if (err == -EINVAL)
+ return NOTIFY_OK;
+ return notifier_from_errno(err) | NOTIFY_STOP_MASK;
+}
+
#endif
@@ -36,8 +36,8 @@
*
*/
-#ifndef __LIBCFS_IOCTL_H__
-#define __LIBCFS_IOCTL_H__
+#ifndef __UAPI_LIBCFS_IOCTL_H__
+#define __UAPI_LIBCFS_IOCTL_H__
#include <linux/types.h>
#include <linux/ioctl.h>
@@ -49,8 +49,8 @@
#define __user
#endif
-#define LIBCFS_IOCTL_VERSION 0x0001000a
-#define LIBCFS_IOCTL_VERSION2 0x0001000b
+#define LNET_IOCTL_VERSION 0x0001000a
+#define LNET_IOCTL_VERSION2 0x0001000b
struct libcfs_ioctl_hdr {
__u32 ioc_len;
@@ -159,4 +159,4 @@ struct libcfs_ioctl_data {
#define IOC_LIBCFS_SET_PEER _IOWR(IOC_LIBCFS_TYPE, 112, IOCTL_CONFIG_SIZE)
#define IOC_LIBCFS_MAX_NR 112
-#endif /* __LIBCFS_IOCTL_H__ */
+#endif /* __UAPI_LIBCFS_IOCTL_H__ */
@@ -29,7 +29,6 @@
/*
* This file is part of Lustre, http://www.lustre.org/
*/
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -65,230 +64,26 @@ struct lnet_debugfs_symlink_def {
static struct dentry *lnet_debugfs_root;
-BLOCKING_NOTIFIER_HEAD(libcfs_ioctl_list);
-EXPORT_SYMBOL(libcfs_ioctl_list);
-
-static inline size_t libcfs_ioctl_packlen(struct libcfs_ioctl_data *data)
-{
- size_t len = sizeof(*data);
-
- len += round_up(data->ioc_inllen1, 8);
- len += round_up(data->ioc_inllen2, 8);
- return len;
-}
-
-static inline bool libcfs_ioctl_is_invalid(struct libcfs_ioctl_data *data)
-{
- const int maxlen = 1 << 30;
-
- if (data->ioc_hdr.ioc_len > maxlen)
- return true;
-
- if (data->ioc_inllen1 > maxlen)
- return true;
-
- if (data->ioc_inllen2 > maxlen)
- return true;
-
- if (data->ioc_inlbuf1 && !data->ioc_inllen1) {
- CERROR("LIBCFS ioctl: inlbuf1 pointer but 0 length\n");
- return true;
- }
- if (data->ioc_inlbuf2 && !data->ioc_inllen2) {
- CERROR("LIBCFS ioctl: inlbuf2 pointer but 0 length\n");
- return true;
- }
- if (data->ioc_pbuf1 && !data->ioc_plen1) {
- CERROR("LIBCFS ioctl: pbuf1 pointer but 0 length\n");
- return true;
- }
- if (data->ioc_pbuf2 && !data->ioc_plen2) {
- CERROR("LIBCFS ioctl: pbuf2 pointer but 0 length\n");
- return true;
- }
- if (data->ioc_plen1 && !data->ioc_pbuf1) {
- CERROR("LIBCFS ioctl: plen1 nonzero but no pbuf1 pointer\n");
- return true;
- }
- if (data->ioc_plen2 && !data->ioc_pbuf2) {
- CERROR("LIBCFS ioctl: plen2 nonzero but no pbuf2 pointer\n");
- return true;
- }
- if ((u32)libcfs_ioctl_packlen(data) != data->ioc_hdr.ioc_len) {
- CERROR("LIBCFS ioctl: packlen != ioc_len\n");
- return true;
- }
- if (data->ioc_inllen1 &&
- data->ioc_bulk[data->ioc_inllen1 - 1] != '\0') {
- CERROR("LIBCFS ioctl: inlbuf1 not 0 terminated\n");
- return true;
- }
- if (data->ioc_inllen2 &&
- data->ioc_bulk[round_up(data->ioc_inllen1, 8) +
- data->ioc_inllen2 - 1] != '\0') {
- CERROR("LIBCFS ioctl: inlbuf2 not 0 terminated\n");
- return true;
- }
- return false;
-}
-
-static int libcfs_ioctl_data_adjust(struct libcfs_ioctl_data *data)
-{
- if (libcfs_ioctl_is_invalid(data)) {
- CERROR("libcfs ioctl: parameter not correctly formatted\n");
- return -EINVAL;
- }
-
- if (data->ioc_inllen1)
- data->ioc_inlbuf1 = &data->ioc_bulk[0];
-
- if (data->ioc_inllen2)
- data->ioc_inlbuf2 = &data->ioc_bulk[0] +
- round_up(data->ioc_inllen1, 8);
-
- return 0;
-}
-
-static int libcfs_ioctl_getdata(struct libcfs_ioctl_hdr **hdr_pp,
- const struct libcfs_ioctl_hdr __user *uhdr)
-{
- struct libcfs_ioctl_hdr hdr;
- int err;
-
- if (copy_from_user(&hdr, uhdr, sizeof(hdr)))
- return -EFAULT;
-
- if (hdr.ioc_version != LIBCFS_IOCTL_VERSION &&
- hdr.ioc_version != LIBCFS_IOCTL_VERSION2) {
- CERROR("libcfs ioctl: version mismatch expected %#x, got %#x\n",
- LIBCFS_IOCTL_VERSION, hdr.ioc_version);
- return -EINVAL;
- }
-
- if (hdr.ioc_len < sizeof(hdr)) {
- CERROR("libcfs ioctl: user buffer too small for ioctl\n");
- return -EINVAL;
- }
-
- if (hdr.ioc_len > LIBCFS_IOC_DATA_MAX) {
- CERROR("libcfs ioctl: user buffer is too large %d/%d\n",
- hdr.ioc_len, LIBCFS_IOC_DATA_MAX);
- return -EINVAL;
- }
-
- *hdr_pp = kvmalloc(hdr.ioc_len, GFP_KERNEL);
- if (!*hdr_pp)
- return -ENOMEM;
-
- if (copy_from_user(*hdr_pp, uhdr, hdr.ioc_len)) {
- err = -EFAULT;
- goto free;
- }
-
- if ((*hdr_pp)->ioc_version != hdr.ioc_version ||
- (*hdr_pp)->ioc_len != hdr.ioc_len) {
- err = -EINVAL;
- goto free;
- }
-
- return 0;
-
-free:
- kvfree(*hdr_pp);
- return err;
-}
-
-static int libcfs_ioctl(unsigned long cmd, void __user *uparam)
+int libcfs_ioctl(unsigned int cmd, struct libcfs_ioctl_data *data)
{
- struct libcfs_ioctl_data *data = NULL;
- struct libcfs_ioctl_hdr *hdr;
- int err;
-
- err = libcfs_setup();
- if (err)
- return err;
- /* 'cmd' and permissions get checked in our arch-specific caller */
- err = libcfs_ioctl_getdata(&hdr, uparam);
- if (err) {
- CDEBUG_LIMIT(D_ERROR,
- "libcfs ioctl: data header error %d\n", err);
- return err;
- }
-
- if (hdr->ioc_version == LIBCFS_IOCTL_VERSION) {
- /*
- * The libcfs_ioctl_data_adjust() function performs adjustment
- * operations on the libcfs_ioctl_data structure to make
- * it usable by the code. This doesn't need to be called
- * for new data structures added.
- */
- data = container_of(hdr, struct libcfs_ioctl_data, ioc_hdr);
- err = libcfs_ioctl_data_adjust(data);
- if (err)
- goto out;
- }
-
- CDEBUG(D_IOCTL, "libcfs ioctl cmd %lu\n", cmd);
switch (cmd) {
case IOC_LIBCFS_CLEAR_DEBUG:
libcfs_debug_clear_buffer();
break;
-
case IOC_LIBCFS_MARK_DEBUG:
if (!data || !data->ioc_inlbuf1 ||
- data->ioc_inlbuf1[data->ioc_inllen1 - 1] != '\0') {
- err = -EINVAL;
- goto out;
- }
+ data->ioc_inlbuf1[data->ioc_inllen1 - 1] != '\0')
+ return -EINVAL;
+
libcfs_debug_mark_buffer(data->ioc_inlbuf1);
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 (copy_to_user(uparam, hdr, hdr->ioc_len) && !err)
- err = -EFAULT;
- break;
- }
-out:
- kvfree(hdr);
- return err;
-}
-
-static long
-libcfs_psdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
-
- if (_IOC_TYPE(cmd) != IOC_LIBCFS_TYPE ||
- _IOC_NR(cmd) < IOC_LIBCFS_MIN_NR ||
- _IOC_NR(cmd) > IOC_LIBCFS_MAX_NR) {
- CDEBUG(D_IOCTL, "invalid ioctl ( type %d, nr %d, size %d )\n",
- _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
return -EINVAL;
}
-
- return libcfs_ioctl(cmd, (void __user *)arg);
+ return 0;
}
-
-static const struct file_operations libcfs_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = libcfs_psdev_ioctl,
-};
-
-static struct miscdevice libcfs_dev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "lnet",
- .fops = &libcfs_fops,
-};
-
-static int libcfs_dev_registered;
+EXPORT_SYMBOL(libcfs_ioctl);
static int proc_dobitmasks(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -836,9 +631,6 @@ int libcfs_setup(void)
if (libcfs_active)
goto out;
- if (!libcfs_dev_registered)
- goto err;
-
rc = libcfs_debug_init(5 * 1024 * 1024);
if (rc < 0) {
pr_err("LustreError: libcfs_debug_init: rc = %d\n", rc);
@@ -880,18 +672,11 @@ EXPORT_SYMBOL(libcfs_setup);
static int libcfs_init(void)
{
- int rc;
-
lnet_insert_debugfs(lnet_table, THIS_MODULE, &debugfs_state);
if (!IS_ERR_OR_NULL(lnet_debugfs_root))
lnet_insert_debugfs_links(lnet_debugfs_symlinks);
- rc = misc_register(&libcfs_dev);
- if (rc)
- CERROR("misc_register: error %d\n", rc);
- else
- libcfs_dev_registered = 1;
- return rc;
+ return 0;
}
static void libcfs_exit(void)
@@ -909,9 +694,6 @@ static void libcfs_exit(void)
cfs_crypto_unregister();
- if (libcfs_dev_registered)
- misc_deregister(&libcfs_dev);
-
cfs_cpu_fini();
/* the below message is checked in test-framework.sh check_mem_leak() */
@@ -4470,6 +4470,7 @@ LNetCtl(unsigned int cmd, void *arg)
if (rc < 0)
goto report_ping_err;
count = rc;
+ rc = 0;
for (i = 0; i < count; i++) {
struct lnet_processid *result;
@@ -4631,7 +4632,7 @@ LNetCtl(unsigned int cmd, void *arg)
rc = ni->ni_net->net_lnd->lnd_ctl(ni, cmd, arg);
lnet_ni_decref(ni);
- return rc;
+ return rc <= 0 ? rc : 0;
}
/* not reached */
}
@@ -32,6 +32,7 @@
#define DEBUG_SUBSYSTEM S_LNET
+#include <linux/miscdevice.h>
#include <linux/lnet/lib-lnet.h>
#include <uapi/linux/lnet/lnet-dlc.h>
@@ -178,11 +179,9 @@ lnet_dyn_unconfigure_ni(struct libcfs_ioctl_hdr *hdr)
}
static int
-lnet_ioctl(struct notifier_block *nb,
- unsigned long cmd, void *vdata)
+lnet_ioctl(unsigned int cmd, struct libcfs_ioctl_hdr *hdr)
{
int rc;
- struct libcfs_ioctl_hdr *hdr = vdata;
switch (cmd) {
case IOC_LIBCFS_CONFIGURE: {
@@ -219,8 +218,7 @@ lnet_ioctl(struct notifier_block *nb,
break;
default:
- /*
- * Passing LNET_PID_ANY only gives me a ref if the net is up
+ /* Passing LNET_PID_ANY only gives me a ref if the net is up
* already; I'll need it to ensure the net can't go down while
* I'm called into it
*/
@@ -231,11 +229,198 @@ lnet_ioctl(struct notifier_block *nb,
}
break;
}
- return notifier_from_ioctl_errno(rc);
+ return rc;
+}
+
+BLOCKING_NOTIFIER_HEAD(lnet_ioctl_list);
+EXPORT_SYMBOL(lnet_ioctl_list);
+
+static inline size_t lnet_ioctl_packlen(struct libcfs_ioctl_data *data)
+{
+ size_t len = sizeof(*data);
+
+ len += round_up(data->ioc_inllen1, 8);
+ len += round_up(data->ioc_inllen2, 8);
+ return len;
+}
+
+static bool lnet_ioctl_is_invalid(struct libcfs_ioctl_data *data)
+{
+ const int maxlen = 1 << 30;
+
+ if (data->ioc_hdr.ioc_len > maxlen)
+ return true;
+
+ if (data->ioc_inllen1 > maxlen)
+ return true;
+
+ if (data->ioc_inllen2 > maxlen)
+ return true;
+
+ if (data->ioc_inlbuf1 && !data->ioc_inllen1)
+ return true;
+
+ if (data->ioc_inlbuf2 && !data->ioc_inllen2)
+ return true;
+
+ if (data->ioc_pbuf1 && !data->ioc_plen1)
+ return true;
+
+ if (data->ioc_pbuf2 && !data->ioc_plen2)
+ return true;
+
+ if (data->ioc_plen1 && !data->ioc_pbuf1)
+ return true;
+
+ if (data->ioc_plen2 && !data->ioc_pbuf2)
+ return true;
+
+ if (lnet_ioctl_packlen(data) != data->ioc_hdr.ioc_len)
+ return true;
+
+ if (data->ioc_inllen1 &&
+ data->ioc_bulk[round_up(data->ioc_inllen1, 8) +
+ data->ioc_inllen2 - 1] != '\0')
+ return true;
+
+ return false;
+}
+
+static int lnet_ioctl_data_adjust(struct libcfs_ioctl_data *data)
+{
+ if (lnet_ioctl_is_invalid(data)) {
+ CERROR("lnet ioctl: parameter not correctly formatted\n");
+ return -EINVAL;
+ }
+
+ if (data->ioc_inllen1 != 0)
+ data->ioc_inlbuf1 = &data->ioc_bulk[0];
+
+ if (data->ioc_inllen2 != 0)
+ data->ioc_inlbuf2 = (&data->ioc_bulk[0] +
+ round_up(data->ioc_inllen1, 8));
+
+ return 0;
+}
+
+static int lnet_ioctl_getdata(struct libcfs_ioctl_hdr **hdr_pp,
+ struct libcfs_ioctl_hdr __user *uhdr)
+{
+ struct libcfs_ioctl_hdr hdr;
+ int err;
+
+ if (copy_from_user(&hdr, uhdr, sizeof(hdr)))
+ return -EFAULT;
+
+ if (hdr.ioc_version != LNET_IOCTL_VERSION &&
+ hdr.ioc_version != LNET_IOCTL_VERSION2) {
+ CERROR("lnet ioctl: version mismatch expected %#x, got %#x\n",
+ LNET_IOCTL_VERSION, hdr.ioc_version);
+ return -EINVAL;
+ }
+
+ if (hdr.ioc_len < sizeof(struct libcfs_ioctl_hdr)) {
+ CERROR("lnet ioctl: user buffer too small for ioctl\n");
+ return -EINVAL;
+ }
+
+ if (hdr.ioc_len > LIBCFS_IOC_DATA_MAX) {
+ CERROR("lnet ioctl: user buffer is too large %d/%d\n",
+ hdr.ioc_len, LIBCFS_IOC_DATA_MAX);
+ return -EINVAL;
+ }
+
+ *hdr_pp = kmalloc(hdr.ioc_len, GFP_KERNEL);
+ if (!*hdr_pp)
+ return -ENOMEM;
+
+ if (copy_from_user(*hdr_pp, uhdr, hdr.ioc_len)) {
+ err = -EFAULT;
+ goto free;
+ }
+
+ if ((*hdr_pp)->ioc_version != hdr.ioc_version ||
+ (*hdr_pp)->ioc_len != hdr.ioc_len) {
+ err = -EINVAL;
+ goto free;
+ }
+
+ return 0;
+
+free:
+ kfree(*hdr_pp);
+ return err;
+}
+
+static long
+lnet_psdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *uparam = (void __user *)arg;
+ struct libcfs_ioctl_data *data = NULL;
+ struct libcfs_ioctl_hdr *hdr;
+ int err;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (_IOC_TYPE(cmd) != IOC_LIBCFS_TYPE ||
+ _IOC_NR(cmd) < IOC_LIBCFS_MIN_NR ||
+ _IOC_NR(cmd) > IOC_LIBCFS_MAX_NR) {
+ CDEBUG(D_IOCTL, "invalid ioctl ( type %d, nr %d, size %d )\n",
+ _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
+ return -EINVAL;
+ }
+
+ /* 'cmd' and permissions get checked in our arch-specific caller */
+ err = lnet_ioctl_getdata(&hdr, uparam);
+ if (err != 0) {
+ CDEBUG_LIMIT(D_ERROR,
+ "lnet ioctl: data header error %d\n", err);
+ return err;
+ }
+
+ if (hdr->ioc_version == LNET_IOCTL_VERSION) {
+ /* The lnet_ioctl_data_adjust() function performs adjustment
+ * operations on the libcfs_ioctl_data structure to make
+ * it usable by the code. This doesn't need to be called
+ * for new data structures added.
+ */
+ data = container_of(hdr, struct libcfs_ioctl_data, ioc_hdr);
+ err = lnet_ioctl_data_adjust(data);
+ if (err != 0)
+ goto out;
+ }
+
+ CDEBUG(D_IOCTL, "lnet ioctl cmd %u\n", cmd);
+
+ err = libcfs_ioctl(cmd, data);
+ if (err == -EINVAL)
+ err = lnet_ioctl(cmd, hdr);
+ if (err == -EINVAL) {
+ err = blocking_notifier_call_chain(&lnet_ioctl_list,
+ cmd, hdr);
+ if (!(err & NOTIFY_STOP_MASK))
+ /* No-one claimed the ioctl */
+ err = -EINVAL;
+ else
+ err = notifier_to_errno(err);
+ }
+ if (copy_to_user(uparam, hdr, hdr->ioc_len) && !err)
+ err = -EFAULT;
+out:
+ kfree(hdr);
+ return err;
}
-static struct notifier_block lnet_ioctl_handler = {
- .notifier_call = lnet_ioctl,
+static const struct file_operations lnet_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = lnet_psdev_ioctl,
+};
+
+static struct miscdevice lnet_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "lnet",
+ .fops = &lnet_fops,
};
static int __init lnet_init(void)
@@ -248,7 +433,13 @@ static int __init lnet_init(void)
rc = lnet_lib_init();
if (rc) {
- CERROR("lnet_lib_init: error %d\n", rc);
+ CERROR("lnet_lib_init: rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = misc_register(&lnet_dev);
+ if (rc) {
+ CERROR("misc_register: rc = %d\n", rc);
return rc;
}
@@ -256,10 +447,6 @@ static int __init lnet_init(void)
dead_router_check_interval != INT_MIN)
LCONSOLE_WARN("live_router_check_interval and dead_router_check_interval have been deprecated. Use alive_router_check_interval instead. Ignoring these deprecated parameters.\n");
- rc = blocking_notifier_chain_register(&libcfs_ioctl_list,
- &lnet_ioctl_handler);
- LASSERT(!rc);
-
if (config_on_load) {
/*
* Have to schedule a separate thread to avoid deadlocking
@@ -273,12 +460,7 @@ static int __init lnet_init(void)
static void __exit lnet_exit(void)
{
- int rc;
-
- rc = blocking_notifier_chain_unregister(&libcfs_ioctl_list,
- &lnet_ioctl_handler);
- LASSERT(!rc);
-
+ misc_deregister(&lnet_dev);
lnet_router_exit();
lnet_lib_exit();
}
@@ -1989,7 +1989,7 @@ lstcon_console_init(void)
if (rc < 0)
goto out;
- rc = blocking_notifier_chain_register(&libcfs_ioctl_list,
+ rc = blocking_notifier_chain_register(&lnet_ioctl_list,
&lstcon_ioctl_handler);
if (rc < 0) {
@@ -2016,7 +2016,7 @@ lstcon_console_fini(void)
{
int i;
- blocking_notifier_chain_unregister(&libcfs_ioctl_list,
+ blocking_notifier_chain_unregister(&lnet_ioctl_list,
&lstcon_ioctl_handler);
lstcon_fini_netlink();