Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/infiniband/core/netlink.c
37212 views
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
}
191
192