diff options
Diffstat (limited to 'drivers/infiniband/core/netlink.c')
-rw-r--r-- | drivers/infiniband/core/netlink.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c new file mode 100644 index 000000000000..4a5abaf0a25c --- /dev/null +++ b/drivers/infiniband/core/netlink.c | |||
@@ -0,0 +1,190 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Voltaire Inc. All rights reserved. | ||
3 | * | ||
4 | * This software is available to you under a choice of one of two | ||
5 | * licenses. You may choose to be licensed under the terms of the GNU | ||
6 | * General Public License (GPL) Version 2, available from the file | ||
7 | * COPYING in the main directory of this source tree, or the | ||
8 | * OpenIB.org BSD license below: | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or | ||
11 | * without modification, are permitted provided that the following | ||
12 | * conditions are met: | ||
13 | * | ||
14 | * - Redistributions of source code must retain the above | ||
15 | * copyright notice, this list of conditions and the following | ||
16 | * disclaimer. | ||
17 | * | ||
18 | * - Redistributions in binary form must reproduce the above | ||
19 | * copyright notice, this list of conditions and the following | ||
20 | * disclaimer in the documentation and/or other materials | ||
21 | * provided with the distribution. | ||
22 | * | ||
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
30 | * SOFTWARE. | ||
31 | */ | ||
32 | |||
33 | #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ | ||
34 | |||
35 | #include <net/netlink.h> | ||
36 | #include <net/net_namespace.h> | ||
37 | #include <net/sock.h> | ||
38 | #include <rdma/rdma_netlink.h> | ||
39 | |||
40 | struct ibnl_client { | ||
41 | struct list_head list; | ||
42 | int index; | ||
43 | int nops; | ||
44 | const struct ibnl_client_cbs *cb_table; | ||
45 | }; | ||
46 | |||
47 | static DEFINE_MUTEX(ibnl_mutex); | ||
48 | static struct sock *nls; | ||
49 | static LIST_HEAD(client_list); | ||
50 | |||
51 | int ibnl_add_client(int index, int nops, | ||
52 | const struct ibnl_client_cbs cb_table[]) | ||
53 | { | ||
54 | struct ibnl_client *cur; | ||
55 | struct ibnl_client *nl_client; | ||
56 | |||
57 | nl_client = kmalloc(sizeof *nl_client, GFP_KERNEL); | ||
58 | if (!nl_client) | ||
59 | return -ENOMEM; | ||
60 | |||
61 | nl_client->index = index; | ||
62 | nl_client->nops = nops; | ||
63 | nl_client->cb_table = cb_table; | ||
64 | |||
65 | mutex_lock(&ibnl_mutex); | ||
66 | |||
67 | list_for_each_entry(cur, &client_list, list) { | ||
68 | if (cur->index == index) { | ||
69 | pr_warn("Client for %d already exists\n", index); | ||
70 | mutex_unlock(&ibnl_mutex); | ||
71 | kfree(nl_client); | ||
72 | return -EINVAL; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | list_add_tail(&nl_client->list, &client_list); | ||
77 | |||
78 | mutex_unlock(&ibnl_mutex); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | EXPORT_SYMBOL(ibnl_add_client); | ||
83 | |||
84 | int ibnl_remove_client(int index) | ||
85 | { | ||
86 | struct ibnl_client *cur, *next; | ||
87 | |||
88 | mutex_lock(&ibnl_mutex); | ||
89 | list_for_each_entry_safe(cur, next, &client_list, list) { | ||
90 | if (cur->index == index) { | ||
91 | list_del(&(cur->list)); | ||
92 | mutex_unlock(&ibnl_mutex); | ||
93 | kfree(cur); | ||
94 | return 0; | ||
95 | } | ||
96 | } | ||
97 | pr_warn("Can't remove callback for client idx %d. Not found\n", index); | ||
98 | mutex_unlock(&ibnl_mutex); | ||
99 | |||
100 | return -EINVAL; | ||
101 | } | ||
102 | EXPORT_SYMBOL(ibnl_remove_client); | ||
103 | |||
104 | void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq, | ||
105 | int len, int client, int op) | ||
106 | { | ||
107 | unsigned char *prev_tail; | ||
108 | |||
109 | prev_tail = skb_tail_pointer(skb); | ||
110 | *nlh = NLMSG_NEW(skb, 0, seq, RDMA_NL_GET_TYPE(client, op), | ||
111 | len, NLM_F_MULTI); | ||
112 | (*nlh)->nlmsg_len = skb_tail_pointer(skb) - prev_tail; | ||
113 | return NLMSG_DATA(*nlh); | ||
114 | |||
115 | nlmsg_failure: | ||
116 | nlmsg_trim(skb, prev_tail); | ||
117 | return NULL; | ||
118 | } | ||
119 | EXPORT_SYMBOL(ibnl_put_msg); | ||
120 | |||
121 | int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh, | ||
122 | int len, void *data, int type) | ||
123 | { | ||
124 | unsigned char *prev_tail; | ||
125 | |||
126 | prev_tail = skb_tail_pointer(skb); | ||
127 | NLA_PUT(skb, type, len, data); | ||
128 | nlh->nlmsg_len += skb_tail_pointer(skb) - prev_tail; | ||
129 | return 0; | ||
130 | |||
131 | nla_put_failure: | ||
132 | nlmsg_trim(skb, prev_tail - nlh->nlmsg_len); | ||
133 | return -EMSGSIZE; | ||
134 | } | ||
135 | EXPORT_SYMBOL(ibnl_put_attr); | ||
136 | |||
137 | static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | ||
138 | { | ||
139 | struct ibnl_client *client; | ||
140 | int type = nlh->nlmsg_type; | ||
141 | int index = RDMA_NL_GET_CLIENT(type); | ||
142 | int op = RDMA_NL_GET_OP(type); | ||
143 | |||
144 | list_for_each_entry(client, &client_list, list) { | ||
145 | if (client->index == index) { | ||
146 | if (op < 0 || op >= client->nops || | ||
147 | !client->cb_table[RDMA_NL_GET_OP(op)].dump) | ||
148 | return -EINVAL; | ||
149 | return netlink_dump_start(nls, skb, nlh, | ||
150 | client->cb_table[op].dump, | ||
151 | NULL); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | pr_info("Index %d wasn't found in client list\n", index); | ||
156 | return -EINVAL; | ||
157 | } | ||
158 | |||
159 | static void ibnl_rcv(struct sk_buff *skb) | ||
160 | { | ||
161 | mutex_lock(&ibnl_mutex); | ||
162 | netlink_rcv_skb(skb, &ibnl_rcv_msg); | ||
163 | mutex_unlock(&ibnl_mutex); | ||
164 | } | ||
165 | |||
166 | int __init ibnl_init(void) | ||
167 | { | ||
168 | nls = netlink_kernel_create(&init_net, NETLINK_RDMA, 0, ibnl_rcv, | ||
169 | NULL, THIS_MODULE); | ||
170 | if (!nls) { | ||
171 | pr_warn("Failed to create netlink socket\n"); | ||
172 | return -ENOMEM; | ||
173 | } | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | void ibnl_cleanup(void) | ||
179 | { | ||
180 | struct ibnl_client *cur, *next; | ||
181 | |||
182 | mutex_lock(&ibnl_mutex); | ||
183 | list_for_each_entry_safe(cur, next, &client_list, list) { | ||
184 | list_del(&(cur->list)); | ||
185 | kfree(cur); | ||
186 | } | ||
187 | mutex_unlock(&ibnl_mutex); | ||
188 | |||
189 | netlink_kernel_release(nls); | ||
190 | } | ||