RDMA: Add netlink infrastructure

Add basic RDMA netlink infrastructure that allows for registration of
RDMA clients for which data is to be exported and supplies message
construction callbacks.

Signed-off-by: Nir Muchtar <nirm@voltaire.com>

[ Reorganize a few things, add CONFIG_NET dependency.  - Roland ]

Signed-off-by: Roland Dreier <roland@purestorage.com>
This commit is contained in:
Roland Dreier 2011-05-20 11:46:11 -07:00
parent fd75c789ab
commit b2cbae2c24
7 changed files with 271 additions and 4 deletions

View file

@ -2,6 +2,7 @@ menuconfig INFINIBAND
tristate "InfiniBand support"
depends on PCI || BROKEN
depends on HAS_IOMEM
depends on NET
---help---
Core support for InfiniBand (IB). Make sure to also select
any protocols you wish to use as well as drivers for your

View file

@ -8,7 +8,7 @@ obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o \
$(user_access-y)
ib_core-y := packer.o ud_header.o verbs.o sysfs.o \
device.o fmr_pool.o cache.o
device.o fmr_pool.o cache.o netlink.o
ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
ib_mad-y := mad.o smi.o agent.o mad_rmpp.o

View file

@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <rdma/rdma_netlink.h>
#include "core_priv.h"
@ -730,14 +731,23 @@ static int __init ib_core_init(void)
goto err;
}
ret = ib_cache_setup();
ret = ibnl_init();
if (ret) {
printk(KERN_WARNING "Couldn't set up InfiniBand P_Key/GID cache\n");
printk(KERN_WARNING "Couldn't init IB netlink interface\n");
goto err_sysfs;
}
ret = ib_cache_setup();
if (ret) {
printk(KERN_WARNING "Couldn't set up InfiniBand P_Key/GID cache\n");
goto err_nl;
}
return 0;
err_nl:
ibnl_cleanup();
err_sysfs:
ib_sysfs_cleanup();
@ -749,6 +759,7 @@ static int __init ib_core_init(void)
static void __exit ib_core_cleanup(void)
{
ib_cache_cleanup();
ibnl_cleanup();
ib_sysfs_cleanup();
/* Make sure that any pending umem accounting work is done. */
destroy_workqueue(ib_wq);

View file

@ -0,0 +1,190 @@
/*
* 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 <net/netlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <rdma/rdma_netlink.h>
struct ibnl_client {
struct list_head list;
int index;
int nops;
const struct ibnl_client_cbs *cb_table;
};
static DEFINE_MUTEX(ibnl_mutex);
static struct sock *nls;
static LIST_HEAD(client_list);
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);
if (!nl_client)
return -ENOMEM;
nl_client->index = index;
nl_client->nops = 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;
}
}
list_add_tail(&nl_client->list, &client_list);
mutex_unlock(&ibnl_mutex);
return 0;
}
EXPORT_SYMBOL(ibnl_add_client);
int ibnl_remove_client(int index)
{
struct ibnl_client *cur, *next;
mutex_lock(&ibnl_mutex);
list_for_each_entry_safe(cur, next, &client_list, list) {
if (cur->index == index) {
list_del(&(cur->list));
mutex_unlock(&ibnl_mutex);
kfree(cur);
return 0;
}
}
pr_warn("Can't remove callback for client idx %d. Not found\n", index);
mutex_unlock(&ibnl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL(ibnl_remove_client);
void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq,
int len, int client, int op)
{
unsigned char *prev_tail;
prev_tail = skb_tail_pointer(skb);
*nlh = NLMSG_NEW(skb, 0, seq, RDMA_NL_GET_TYPE(client, 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_msg);
int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
int len, void *data, int type)
{
unsigned char *prev_tail;
prev_tail = skb_tail_pointer(skb);
NLA_PUT(skb, type, len, data);
nlh->nlmsg_len += skb_tail_pointer(skb) - prev_tail;
return 0;
nla_put_failure:
nlmsg_trim(skb, prev_tail - nlh->nlmsg_len);
return -EMSGSIZE;
}
EXPORT_SYMBOL(ibnl_put_attr);
static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct ibnl_client *client;
int type = nlh->nlmsg_type;
int index = RDMA_NL_GET_CLIENT(type);
int op = RDMA_NL_GET_OP(type);
list_for_each_entry(client, &client_list, list) {
if (client->index == index) {
if (op < 0 || op >= client->nops ||
!client->cb_table[RDMA_NL_GET_OP(op)].dump)
return -EINVAL;
return netlink_dump_start(nls, skb, nlh,
client->cb_table[op].dump,
NULL);
}
}
pr_info("Index %d wasn't found in client list\n", index);
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 __init ibnl_init(void)
{
nls = netlink_kernel_create(&init_net, NETLINK_RDMA, 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_client *cur, *next;
mutex_lock(&ibnl_mutex);
list_for_each_entry_safe(cur, next, &client_list, list) {
list_del(&(cur->list));
kfree(cur);
}
mutex_unlock(&ibnl_mutex);
netlink_kernel_release(nls);
}

View file

@ -1,6 +1,6 @@
config INFINIBAND_QIB
tristate "QLogic PCIe HCA support"
depends on 64BIT && NET
depends on 64BIT
---help---
This is a low-level driver for QLogic PCIe QLE InfiniBand host
channel adapters. This driver does not support the QLogic

View file

@ -24,6 +24,7 @@
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
#define MAX_LINKS 32

View file

@ -0,0 +1,64 @@
#ifndef _RDMA_NETLINK_H
#define _RDMA_NETLINK_H
#define RDMA_NL_GET_CLIENT(type) ((type & (((1 << 6) - 1) << 10)) >> 10)
#define RDMA_NL_GET_OP(type) (type & ((1 << 10) - 1))
#define RDMA_NL_GET_TYPE(client, op) ((client << 10) + op)
#ifdef __KERNEL__
#include <linux/netlink.h>
struct ibnl_client_cbs {
int (*dump)(struct sk_buff *skb, struct netlink_callback *nlcb);
};
int ibnl_init(void);
void ibnl_cleanup(void);
/**
* Add a a client to the list of IB netlink exporters.
* @index: Index of the added client
* @nops: Number of supported ops by the added client.
* @cb_table: A table for op->callback
*
* Returns 0 on success or a negative error code.
*/
int ibnl_add_client(int index, int nops,
const struct ibnl_client_cbs cb_table[]);
/**
* Remove a client from IB netlink.
* @index: Index of the removed IB client.
*
* Returns 0 on success or a negative error code.
*/
int ibnl_remove_client(int index);
/**
* Put a new message in a supplied skb.
* @skb: The netlink skb.
* @nlh: Pointer to put the header of the new netlink message.
* @seq: The message sequence number.
* @len: The requested message length to allocate.
* @client: Calling IB netlink client.
* @op: message content op.
* Returns the allocated buffer on success and NULL on failure.
*/
void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq,
int len, int client, int op);
/**
* Put a new attribute in a supplied skb.
* @skb: The netlink skb.
* @nlh: Header of the netlink message to append the attribute to.
* @len: The length of the attribute data.
* @data: The attribute data to put.
* @type: The attribute type.
* Returns the 0 and a negative error code on failure.
*/
int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
int len, void *data, int type);
#endif /* __KERNEL__ */
#endif /* _RDMA_NETLINK_H */