@@ -4534,5 +4534,7 @@ static void __exit cma_cleanup(void)
destroy_workqueue(cma_wq);
}
+MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_RDMA_CM, 1);
+
module_init(cma_init);
module_exit(cma_cleanup);
@@ -1204,5 +1204,7 @@ static void __exit ib_core_cleanup(void)
destroy_workqueue(ib_wq);
}
+MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_LS, 4);
+
module_init(ib_core_init);
module_exit(ib_core_cleanup);
@@ -1204,5 +1204,7 @@ static void __exit iw_cm_cleanup(void)
iwpm_exit(RDMA_NL_IWCM);
}
+MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_IWCM, 2);
+
module_init(iw_cm_init);
module_exit(iw_cm_cleanup);
@@ -51,6 +51,18 @@ static DEFINE_MUTEX(ibnl_mutex);
static struct sock *nls;
static LIST_HEAD(client_list);
+static struct ibnl_client *ibnl_find_client(int index)
+{
+ struct ibnl_client *client;
+
+ list_for_each_entry_rcu(client, &client_list, list) {
+ if (client->index == index)
+ return client;
+ }
+
+ return NULL;
+}
+
int ibnl_chk_listeners(unsigned int group)
{
if (netlink_has_listeners(nls, group) == 0)
@@ -61,7 +73,6 @@ int ibnl_chk_listeners(unsigned int group)
int ibnl_add_client(int index, int nops,
const struct ibnl_client_cbs cb_table[])
{
- struct ibnl_client *cur;
struct ibnl_client *nl_client;
nl_client = kmalloc(sizeof *nl_client, GFP_KERNEL);
@@ -73,14 +84,11 @@ int ibnl_add_client(int index, int nops,
nl_client->cb_table = cb_table;
mutex_lock(&ibnl_mutex);
-
- list_for_each_entry(cur, &client_list, list) {
- if (cur->index == index) {
- pr_warn("Client for %d already exists\n", index);
- mutex_unlock(&ibnl_mutex);
- kfree(nl_client);
- return -EINVAL;
- }
+ if (ibnl_find_client(index)) {
+ pr_warn("Client for %d already exists\n", index);
+ mutex_unlock(&ibnl_mutex);
+ kfree(nl_client);
+ return -EINVAL;
}
list_add_tail(&nl_client->list, &client_list);
@@ -155,40 +163,46 @@ static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
int index = RDMA_NL_GET_CLIENT(type);
unsigned int op = RDMA_NL_GET_OP(type);
- list_for_each_entry(client, &client_list, list) {
- if (client->index == index) {
- if (op >= client->nops || !client->cb_table[op].dump)
- return -EINVAL;
-
- /*
- * For response or local service set_timeout request,
- * there is no need to use netlink_dump_start.
- */
- if (!(nlh->nlmsg_flags & NLM_F_REQUEST) ||
- (index == RDMA_NL_LS &&
- op == RDMA_NL_LS_OP_SET_TIMEOUT)) {
- struct netlink_callback cb = {
- .skb = skb,
- .nlh = nlh,
- .dump = client->cb_table[op].dump,
- .module = client->cb_table[op].module,
- };
-
- return cb.dump(skb, &cb);
- }
-
- {
- struct netlink_dump_control c = {
- .dump = client->cb_table[op].dump,
- .module = client->cb_table[op].module,
- };
- return netlink_dump_start(nls, skb, nlh, &c);
- }
- }
+ client = ibnl_find_client(index);
+#ifdef CONFIG_MODULES
+ if (!client) {
+ mutex_unlock(&ibnl_mutex);
+ request_module("rdma_netlink_subsys-%d", index);
+ mutex_lock(&ibnl_mutex);
+ client = ibnl_find_client(index);
+ }
+#endif
+ if (!client) {
+ pr_info("Index %d wasn't found in client list\n", index);
+ return -EINVAL;
}
- pr_info("Index %d wasn't found in client list\n", index);
- return -EINVAL;
+ if (op >= client->nops || !client->cb_table[op].dump)
+ return -EINVAL;
+
+ /*
+ * For response or local service set_timeout request,
+ * there is no need to use netlink_dump_start.
+ */
+ if (!(nlh->nlmsg_flags & NLM_F_REQUEST) ||
+ (index == RDMA_NL_LS && op == RDMA_NL_LS_OP_SET_TIMEOUT)) {
+ struct netlink_callback cb = {
+ .skb = skb,
+ .nlh = nlh,
+ .dump = client->cb_table[op].dump,
+ .module = client->cb_table[op].module,
+ };
+
+ return cb.dump(skb, &cb);
+ }
+
+ {
+ struct netlink_dump_control c = {
+ .dump = client->cb_table[op].dump,
+ .module = client->cb_table[op].module,
+ };
+ return netlink_dump_start(nls, skb, nlh, &c);
+ }
}
static void ibnl_rcv_reply_skb(struct sk_buff *skb)
@@ -10,6 +10,18 @@ struct ibnl_client_cbs {
struct module *module;
};
+/* Define this module as providing netlinkg services for NETLINK_RDMA, client
+ * index _index. Since the client indexes were setup in a uapi header as an
+ * enum and we do no want to change that, the user must supply the expanded
+ * constant as well and the compiler checks they are the same.
+ */
+#define MODULE_ALIAS_RDMA_NETLINK(_index, _val) \
+ static inline void __chk_##_index(void) \
+ { \
+ BUILD_BUG_ON(_index != _val); \
+ } \
+ MODULE_ALIAS("rdma_netlink_subsys-" __stringify(_val))
+
/**
* Add a a client to the list of IB netlink exporters.
* @index: Index of the added client
If a message comes in and we do not have the client in the table, then try to load the module supplying that client using MODULE_ALIAS to find it. This duplicates the scheme seen in other netlink muxes (eg nfnetlink). Factor out ibnl_find_client since this creates a 3rd copy of the pattern. Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> --- drivers/infiniband/core/cma.c | 2 + drivers/infiniband/core/device.c | 2 + drivers/infiniband/core/iwcm.c | 2 + drivers/infiniband/core/netlink.c | 96 ++++++++++++++++++++++----------------- include/rdma/rdma_netlink.h | 12 +++++ 5 files changed, 73 insertions(+), 41 deletions(-)