Skip to content

Commit

Permalink
RDMA: Add netlink infrastructure
Browse files Browse the repository at this point in the history
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 <[email protected]>

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

Signed-off-by: Roland Dreier <[email protected]>
  • Loading branch information
rolandd committed May 20, 2011
1 parent fd75c78 commit b2cbae2
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 3 deletions.
1 change: 1 addition & 0 deletions drivers/infiniband/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion drivers/infiniband/core/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 12 additions & 1 deletion drivers/infiniband/core/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -730,14 +731,23 @@ static int __init ib_core_init(void)
goto err;
}

ret = ibnl_init();
if (ret) {
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_sysfs;
goto err_nl;
}

return 0;

err_nl:
ibnl_cleanup();

err_sysfs:
ib_sysfs_cleanup();

Expand All @@ -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);
Expand Down
190 changes: 190 additions & 0 deletions drivers/infiniband/core/netlink.c
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 1 addition & 1 deletion drivers/infiniband/hw/qib/Kconfig
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions include/linux/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
64 changes: 64 additions & 0 deletions include/rdma/rdma_netlink.h
Original file line number Diff line number Diff line change
@@ -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 */

0 comments on commit b2cbae2

Please sign in to comment.