The basic IB netlink infrastructure. It allows for registration of IB module for which data is to be exported. It supplies skb/message construction callbacks.
Signed-off-by: Nir Muchtar <n...@voltaire.com> --- drivers/infiniband/core/netlink.c | 202 +++++++++++++++++++++++++++++++++++++ include/linux/netlink.h | 1 + include/rdma/ib_netlink.h | 60 +++++++++++ 3 files changed, 263 insertions(+), 0 deletions(-) create mode 100644 drivers/infiniband/core/netlink.c create mode 100644 include/rdma/ib_netlink.h diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c new file mode 100644 index 0000000..4c53666 --- /dev/null +++ b/drivers/infiniband/core/netlink.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2010 Voltaire Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ + +#include <linux/netlink.h> + +#include <net/netlink.h> +#include <net/net_namespace.h> + +#include <rdma/ib_netlink.h> + +struct ibnl_cb { + struct list_head list; + int module; + int (*get_data)(int op, struct sk_buff **nl_skb, int pid); +}; + +static DEFINE_MUTEX(ibnl_mutex); +static struct sock *nls; +static LIST_HEAD(cb_list); + +int ibnl_add_cb(int module, + int (*get_data)(int op, struct sk_buff **skb, int pid)) +{ + struct ibnl_cb *cur; + struct ibnl_cb *nl_cb = kmalloc(sizeof *nl_cb, GFP_KERNEL); + + if (!nl_cb) + return -ENOMEM; + nl_cb->module = module; + nl_cb->get_data = get_data; + mutex_lock(&ibnl_mutex); + list_for_each_entry(cur, &cb_list, list) { + if (cur->module == module) { + pr_warn("Callback for %d already exists\n", module); + mutex_unlock(&ibnl_mutex); + kfree(nl_cb); + return -EINVAL; + } + } + list_add_tail(&nl_cb->list, &cb_list); + mutex_unlock(&ibnl_mutex); + return 0; +} +EXPORT_SYMBOL(ibnl_add_cb); + +int ibnl_remove_cb(int module) +{ + struct ibnl_cb *cur, *next; + + mutex_lock(&ibnl_mutex); + list_for_each_entry_safe(cur, next, &cb_list, list) { + if (cur->module == module) { + list_del(&(cur->list)); + mutex_unlock(&ibnl_mutex); + kfree(cur); + return 0; + } + } + pr_warn("Can't remove callback for module %d. Not found\n", module); + mutex_unlock(&ibnl_mutex); + return -EINVAL; +} +EXPORT_SYMBOL(ibnl_remove_cb); + +static void ibnl_unicast(int pid, struct sk_buff *skb) +{ + netlink_unicast(nls, skb, pid, MSG_DONTWAIT); +} + +static int ibnl_handle_request(int pid, int op, struct ibnl_cb *cb) +{ + int ret; + struct sk_buff **nl_skb; + + nl_skb = kmalloc(sizeof *nl_skb, GFP_KERNEL); + if (!nl_skb) { + pr_info("Couldn't allocate skb\n"); + ret = -ENOMEM; + goto err; + } + *nl_skb = NULL; + ret = cb->get_data(op, nl_skb, pid); + if (ret) { + if (ret != -EMSGSIZE) + goto err; + } + if (*nl_skb) { + ibnl_put(nl_skb, pid, 0, 0, 0, NLMSG_DONE); + ibnl_unicast(pid, *nl_skb); + } + kfree(nl_skb); + return 0; + +err: + kfree(nl_skb); + return ret; +} + +void *ibnl_put(struct sk_buff **skb, int pid, int seq, + int len, int module, int op) +{ + unsigned char *prev_tail; + struct nlmsghdr *nlh; + + if (*skb && skb_tailroom(*skb) < (int)NLMSG_SPACE(len)) { + ibnl_unicast(pid, *skb); + *skb = NULL; + } + if (!*skb) { + *skb = alloc_skb(NLMSG_SPACE(NLMSG_GOODSIZE), GFP_KERNEL); + if (!*skb) { + pr_info("Couldn't allocate skb\n"); + return NULL; + } + } + prev_tail = skb_tail_pointer(*skb); + nlh = NLMSG_NEW(*skb, 0, seq, IBNL_GET_TYPE(module, op), + len, NLM_F_MULTI); + nlh->nlmsg_len = skb_tail_pointer(*skb) - prev_tail; + return NLMSG_DATA(nlh); +nlmsg_failure: + nlmsg_trim(*skb, prev_tail); + return NULL; +} +EXPORT_SYMBOL(ibnl_put); + +static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + struct ibnl_cb *cb; + int type = nlh->nlmsg_type; + int pid = nlh->nlmsg_pid; + int module = IBNL_GET_MODULE(type); + + list_for_each_entry(cb, &cb_list, list) { + if (cb->module == module) + return ibnl_handle_request(pid, IBNL_GET_OP(type), cb); + } + pr_info("Callback for module %d not found\n", module); + return -EINVAL; +} + +static void ibnl_rcv(struct sk_buff *skb) +{ + mutex_lock(&ibnl_mutex); + netlink_rcv_skb(skb, &ibnl_rcv_msg); + mutex_unlock(&ibnl_mutex); +} + +int ibnl_init(void) +{ + nls = netlink_kernel_create(&init_net, NETLINK_INFINIBAND, 0, ibnl_rcv, + NULL, THIS_MODULE); + if (!nls) { + pr_warn("Failed to create netlink socket\n"); + return -ENOMEM; + } + return 0; +} + +void ibnl_cleanup(void) +{ + struct ibnl_cb *cur, *next; + + mutex_lock(&ibnl_mutex); + list_for_each_entry_safe(cur, next, &cb_list, list) { + list_del(&(cur->list)); + kfree(cur); + } + mutex_unlock(&ibnl_mutex); + netlink_kernel_release(nls); +} diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 1235669..c9693f9 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -24,6 +24,7 @@ /* leave room for NETLINK_DM (DM Events) */ #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ #define NETLINK_ECRYPTFS 19 +#define NETLINK_INFINIBAND 20 #define MAX_LINKS 32 diff --git a/include/rdma/ib_netlink.h b/include/rdma/ib_netlink.h new file mode 100644 index 0000000..ebe564d --- /dev/null +++ b/include/rdma/ib_netlink.h @@ -0,0 +1,60 @@ +#ifndef _IBNETLINK_H +#define _IBNETLINK_H + +#include <linux/rtnetlink.h> +#include <rdma/rdma_cm.h> + +enum { + IBNL_RDMA_CM = 1 +}; + +enum { + IBNL_RDMA_CM_STATS = 0, + IBNL_RDMA_CM_DEVICE_NAME, + IBNL_RDMA_CM_ID_STATS, +}; + +#define IBNL_GET_MODULE(type) ((type & (((1 << 6) - 1) << 10)) >> 10) +#define IBNL_GET_OP(type) (type & ((1 << 10) - 1)) +#define IBNL_GET_TYPE(module, op) ((module << 10) + op) + +#ifdef __KERNEL__ + +int ibnl_init(void); +void ibnl_cleanup(void); + +/** + * Add a callback for an IB module to the IB netlink module. + * @module: The added IB module + * @get_size: A callback for obtaining the necessary size for the returned data + * @get_data: A callback for obtaining the data + * + * Returns 0 on success or a negative error code. + */ +int ibnl_add_cb(int module, + int (*get_data)(int op, struct sk_buff **skb, int pid)); + +/** + * Remove a callback for a registered IB module + * @module: The removed IB module + * + * Returns 0 on success or a negative error code. + */ +int ibnl_remove_cb(int module); + +/** + * Put a new message in a supplied skb. + * @skb: The netlink skb. + * @pid: The destination pid + * @seq: The message sequence number. + * @len: The requested message length to allocate. + * @module: Calling IB netlink module. + * @op: message content op. + * Returns the allocated buffer on success and NULL on failure. + */ +void *ibnl_put(struct sk_buff **skb, int pid, int seq, + int len, int module, int op); + +#endif /* __KERNEL__ */ + +#endif /* _IBNETLINK_H */ -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-rdma" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html