blob: 327bf2afe820a379154f477b613aa98deba3da19 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Thomas Richter6812baa2017-01-09 16:55:15 +01002/*
3 * Shared Memory Communications over RDMA (SMC-R) and RoCE
4 *
5 * Generic netlink support functions to configure an SMC-R PNET table
6 *
7 * Copyright IBM Corp. 2016
8 *
9 * Author(s): Thomas Richter <tmricht@linux.vnet.ibm.com>
10 */
11
12#include <linux/module.h>
13#include <linux/list.h>
14#include <linux/ctype.h>
15#include <net/netlink.h>
16#include <net/genetlink.h>
17
18#include <uapi/linux/if.h>
19#include <uapi/linux/smc.h>
20
21#include <rdma/ib_verbs.h>
22
23#include "smc_pnet.h"
24#include "smc_ib.h"
Hans Wippel1619f772018-06-28 19:05:08 +020025#include "smc_ism.h"
Thomas Richter6812baa2017-01-09 16:55:15 +010026
Hans Wippel890a2cb2019-02-21 13:01:00 +010027#define SMC_ASCII_BLANK 32
28
29static struct net_device *pnet_find_base_ndev(struct net_device *ndev);
30
Thomas Richter6812baa2017-01-09 16:55:15 +010031static struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = {
32 [SMC_PNETID_NAME] = {
33 .type = NLA_NUL_STRING,
Hans Wippelca8dc132019-01-30 18:51:01 +010034 .len = SMC_MAX_PNETID_LEN
Thomas Richter6812baa2017-01-09 16:55:15 +010035 },
36 [SMC_PNETID_ETHNAME] = {
37 .type = NLA_NUL_STRING,
38 .len = IFNAMSIZ - 1
39 },
40 [SMC_PNETID_IBNAME] = {
41 .type = NLA_NUL_STRING,
42 .len = IB_DEVICE_NAME_MAX - 1
43 },
44 [SMC_PNETID_IBPORT] = { .type = NLA_U8 }
45};
46
47static struct genl_family smc_pnet_nl_family;
48
49/**
50 * struct smc_pnettable - SMC PNET table anchor
51 * @lock: Lock for list action
52 * @pnetlist: List of PNETIDs
53 */
54static struct smc_pnettable {
55 rwlock_t lock;
56 struct list_head pnetlist;
57} smc_pnettable = {
58 .pnetlist = LIST_HEAD_INIT(smc_pnettable.pnetlist),
59 .lock = __RW_LOCK_UNLOCKED(smc_pnettable.lock)
60};
61
62/**
Hans Wippel890a2cb2019-02-21 13:01:00 +010063 * struct smc_user_pnetentry - pnet identifier name entry for/from user
Thomas Richter6812baa2017-01-09 16:55:15 +010064 * @list: List node.
65 * @pnet_name: Pnet identifier name
66 * @ndev: pointer to network device.
67 * @smcibdev: Pointer to IB device.
Hans Wippel890a2cb2019-02-21 13:01:00 +010068 * @ib_port: Port of IB device.
Thomas Richter6812baa2017-01-09 16:55:15 +010069 */
Hans Wippel890a2cb2019-02-21 13:01:00 +010070struct smc_user_pnetentry {
Thomas Richter6812baa2017-01-09 16:55:15 +010071 struct list_head list;
Ursula Braun0afff912018-06-28 19:05:05 +020072 char pnet_name[SMC_MAX_PNETID_LEN + 1];
Thomas Richter6812baa2017-01-09 16:55:15 +010073 struct net_device *ndev;
74 struct smc_ib_device *smcibdev;
75 u8 ib_port;
76};
77
Hans Wippel890a2cb2019-02-21 13:01:00 +010078/* pnet entry stored in pnet table */
79struct smc_pnetentry {
80 struct list_head list;
81 char pnet_name[SMC_MAX_PNETID_LEN + 1];
82 struct net_device *ndev;
83};
Thomas Richter6812baa2017-01-09 16:55:15 +010084
Hans Wippel890a2cb2019-02-21 13:01:00 +010085/* Check if two given pnetids match */
86static bool smc_pnet_match(u8 *pnetid1, u8 *pnetid2)
Thomas Richter6812baa2017-01-09 16:55:15 +010087{
Hans Wippel890a2cb2019-02-21 13:01:00 +010088 int i;
Thomas Richter6812baa2017-01-09 16:55:15 +010089
Hans Wippel890a2cb2019-02-21 13:01:00 +010090 for (i = 0; i < SMC_MAX_PNETID_LEN; i++) {
91 if ((pnetid1[i] == 0 || pnetid1[i] == SMC_ASCII_BLANK) &&
92 (pnetid2[i] == 0 || pnetid2[i] == SMC_ASCII_BLANK))
Thomas Richter6812baa2017-01-09 16:55:15 +010093 break;
Hans Wippel890a2cb2019-02-21 13:01:00 +010094 if (pnetid1[i] != pnetid2[i])
95 return false;
Thomas Richter6812baa2017-01-09 16:55:15 +010096 }
Hans Wippel890a2cb2019-02-21 13:01:00 +010097 return true;
Thomas Richter6812baa2017-01-09 16:55:15 +010098}
99
100/* Remove a pnetid from the pnet table.
101 */
102static int smc_pnet_remove_by_pnetid(char *pnet_name)
103{
104 struct smc_pnetentry *pnetelem, *tmp_pe;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100105 struct smc_ib_device *ibdev;
Thomas Richter6812baa2017-01-09 16:55:15 +0100106 int rc = -ENOENT;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100107 int ibport;
Thomas Richter6812baa2017-01-09 16:55:15 +0100108
Hans Wippel890a2cb2019-02-21 13:01:00 +0100109 /* remove netdevices */
Thomas Richter6812baa2017-01-09 16:55:15 +0100110 write_lock(&smc_pnettable.lock);
111 list_for_each_entry_safe(pnetelem, tmp_pe, &smc_pnettable.pnetlist,
112 list) {
Hans Wippel890a2cb2019-02-21 13:01:00 +0100113 if (!pnet_name ||
114 smc_pnet_match(pnetelem->pnet_name, pnet_name)) {
Thomas Richter6812baa2017-01-09 16:55:15 +0100115 list_del(&pnetelem->list);
116 dev_put(pnetelem->ndev);
117 kfree(pnetelem);
118 rc = 0;
Thomas Richter6812baa2017-01-09 16:55:15 +0100119 }
120 }
121 write_unlock(&smc_pnettable.lock);
Hans Wippel890a2cb2019-02-21 13:01:00 +0100122 /* remove ib devices */
123 spin_lock(&smc_ib_devices.lock);
124 list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
125 for (ibport = 0; ibport < SMC_MAX_PORTS; ibport++) {
126 if (ibdev->pnetid_by_user[ibport] &&
127 (!pnet_name ||
128 smc_pnet_match(pnet_name,
129 ibdev->pnetid[ibport]))) {
130 memset(ibdev->pnetid[ibport], 0,
131 SMC_MAX_PNETID_LEN);
132 ibdev->pnetid_by_user[ibport] = false;
133 rc = 0;
134 }
135 }
136 }
137 spin_unlock(&smc_ib_devices.lock);
Thomas Richter6812baa2017-01-09 16:55:15 +0100138 return rc;
139}
140
141/* Remove a pnet entry mentioning a given network device from the pnet table.
142 */
143static int smc_pnet_remove_by_ndev(struct net_device *ndev)
144{
145 struct smc_pnetentry *pnetelem, *tmp_pe;
146 int rc = -ENOENT;
147
148 write_lock(&smc_pnettable.lock);
149 list_for_each_entry_safe(pnetelem, tmp_pe, &smc_pnettable.pnetlist,
150 list) {
151 if (pnetelem->ndev == ndev) {
152 list_del(&pnetelem->list);
153 dev_put(pnetelem->ndev);
154 kfree(pnetelem);
155 rc = 0;
156 break;
157 }
158 }
159 write_unlock(&smc_pnettable.lock);
160 return rc;
161}
162
Thomas Richter6812baa2017-01-09 16:55:15 +0100163/* Append a pnetid to the end of the pnet table if not already on this list.
164 */
Hans Wippel890a2cb2019-02-21 13:01:00 +0100165static int smc_pnet_enter(struct smc_user_pnetentry *new_pnetelem)
Thomas Richter6812baa2017-01-09 16:55:15 +0100166{
Hans Wippel890a2cb2019-02-21 13:01:00 +0100167 u8 pnet_null[SMC_MAX_PNETID_LEN] = {0};
168 u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
169 struct smc_pnetentry *tmp_pnetelem;
Thomas Richter6812baa2017-01-09 16:55:15 +0100170 struct smc_pnetentry *pnetelem;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100171 struct net_device *ndev;
172 bool new_netdev = true;
173 bool new_ibdev = false;
174
175 if (new_pnetelem->smcibdev) {
176 struct smc_ib_device *ib_dev = new_pnetelem->smcibdev;
177 int ib_port = new_pnetelem->ib_port;
178
179 spin_lock(&smc_ib_devices.lock);
180 if (smc_pnet_match(ib_dev->pnetid[ib_port - 1], pnet_null)) {
181 memcpy(ib_dev->pnetid[ib_port - 1],
182 new_pnetelem->pnet_name, SMC_MAX_PNETID_LEN);
183 ib_dev->pnetid_by_user[ib_port - 1] = true;
184 new_ibdev = true;
185 }
186 spin_unlock(&smc_ib_devices.lock);
187 }
188
189 if (!new_pnetelem->ndev)
190 return new_ibdev ? 0 : -EEXIST;
191
192 /* check if (base) netdev already has a pnetid. If there is one, we do
193 * not want to add a pnet table entry
194 */
195 ndev = pnet_find_base_ndev(new_pnetelem->ndev);
196 if (!smc_pnetid_by_dev_port(ndev->dev.parent, ndev->dev_port,
197 ndev_pnetid))
198 return new_ibdev ? 0 : -EEXIST;
199
200 /* add a new netdev entry to the pnet table if there isn't one */
201 tmp_pnetelem = kzalloc(sizeof(*pnetelem), GFP_KERNEL);
202 if (!tmp_pnetelem)
203 return -ENOMEM;
204 memcpy(tmp_pnetelem->pnet_name, new_pnetelem->pnet_name,
205 SMC_MAX_PNETID_LEN);
206 tmp_pnetelem->ndev = new_pnetelem->ndev;
Thomas Richter6812baa2017-01-09 16:55:15 +0100207
208 write_lock(&smc_pnettable.lock);
209 list_for_each_entry(pnetelem, &smc_pnettable.pnetlist, list) {
Hans Wippel890a2cb2019-02-21 13:01:00 +0100210 if (pnetelem->ndev == new_pnetelem->ndev)
211 new_netdev = false;
Thomas Richter6812baa2017-01-09 16:55:15 +0100212 }
Hans Wippel890a2cb2019-02-21 13:01:00 +0100213 if (new_netdev) {
214 dev_hold(tmp_pnetelem->ndev);
215 list_add_tail(&tmp_pnetelem->list, &smc_pnettable.pnetlist);
216 write_unlock(&smc_pnettable.lock);
217 } else {
218 write_unlock(&smc_pnettable.lock);
219 kfree(tmp_pnetelem);
220 }
221
222 return (new_netdev || new_ibdev) ? 0 : -EEXIST;
Thomas Richter6812baa2017-01-09 16:55:15 +0100223}
224
225/* The limit for pnetid is 16 characters.
226 * Valid characters should be (single-byte character set) a-z, A-Z, 0-9.
227 * Lower case letters are converted to upper case.
228 * Interior blanks should not be used.
229 */
230static bool smc_pnetid_valid(const char *pnet_name, char *pnetid)
231{
232 char *bf = skip_spaces(pnet_name);
233 size_t len = strlen(bf);
234 char *end = bf + len;
235
236 if (!len)
237 return false;
238 while (--end >= bf && isspace(*end))
239 ;
Ursula Braun0afff912018-06-28 19:05:05 +0200240 if (end - bf >= SMC_MAX_PNETID_LEN)
Thomas Richter6812baa2017-01-09 16:55:15 +0100241 return false;
242 while (bf <= end) {
243 if (!isalnum(*bf))
244 return false;
245 *pnetid++ = islower(*bf) ? toupper(*bf) : *bf;
246 bf++;
247 }
248 *pnetid = '\0';
249 return true;
250}
251
252/* Find an infiniband device by a given name. The device might not exist. */
Ursula Braun249633a2017-04-10 14:57:57 +0200253static struct smc_ib_device *smc_pnet_find_ib(char *ib_name)
Thomas Richter6812baa2017-01-09 16:55:15 +0100254{
255 struct smc_ib_device *ibdev;
256
257 spin_lock(&smc_ib_devices.lock);
258 list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
259 if (!strncmp(ibdev->ibdev->name, ib_name,
260 sizeof(ibdev->ibdev->name))) {
261 goto out;
262 }
263 }
264 ibdev = NULL;
265out:
266 spin_unlock(&smc_ib_devices.lock);
267 return ibdev;
268}
269
270/* Parse the supplied netlink attributes and fill a pnetentry structure.
271 * For ethernet and infiniband device names verify that the devices exist.
272 */
Hans Wippel890a2cb2019-02-21 13:01:00 +0100273static int smc_pnet_fill_entry(struct net *net,
274 struct smc_user_pnetentry *pnetelem,
Thomas Richter6812baa2017-01-09 16:55:15 +0100275 struct nlattr *tb[])
276{
Eric Biggersd49baa72018-05-13 17:01:30 -0700277 char *string, *ibname;
278 int rc;
Thomas Richter6812baa2017-01-09 16:55:15 +0100279
280 memset(pnetelem, 0, sizeof(*pnetelem));
281 INIT_LIST_HEAD(&pnetelem->list);
Eric Biggersd49baa72018-05-13 17:01:30 -0700282
283 rc = -EINVAL;
284 if (!tb[SMC_PNETID_NAME])
285 goto error;
286 string = (char *)nla_data(tb[SMC_PNETID_NAME]);
287 if (!smc_pnetid_valid(string, pnetelem->pnet_name))
288 goto error;
289
290 rc = -EINVAL;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100291 if (tb[SMC_PNETID_ETHNAME]) {
292 string = (char *)nla_data(tb[SMC_PNETID_ETHNAME]);
293 pnetelem->ndev = dev_get_by_name(net, string);
294 if (!pnetelem->ndev)
295 goto error;
296 }
Eric Biggersd49baa72018-05-13 17:01:30 -0700297
298 rc = -EINVAL;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100299 if (tb[SMC_PNETID_IBNAME]) {
300 ibname = (char *)nla_data(tb[SMC_PNETID_IBNAME]);
301 ibname = strim(ibname);
302 pnetelem->smcibdev = smc_pnet_find_ib(ibname);
303 if (!pnetelem->smcibdev)
304 goto error;
305 if (pnetelem->smcibdev) {
306 if (!tb[SMC_PNETID_IBPORT])
307 goto error;
308 pnetelem->ib_port = nla_get_u8(tb[SMC_PNETID_IBPORT]);
309 if (pnetelem->ib_port < 1 ||
310 pnetelem->ib_port > SMC_MAX_PORTS)
311 goto error;
312 }
313 }
Eric Biggersd49baa72018-05-13 17:01:30 -0700314
Thomas Richter6812baa2017-01-09 16:55:15 +0100315 return 0;
316
317error:
318 if (pnetelem->ndev)
319 dev_put(pnetelem->ndev);
320 return rc;
321}
322
323/* Convert an smc_pnetentry to a netlink attribute sequence */
Hans Wippel890a2cb2019-02-21 13:01:00 +0100324static int smc_pnet_set_nla(struct sk_buff *msg,
325 struct smc_user_pnetentry *pnetelem)
Thomas Richter6812baa2017-01-09 16:55:15 +0100326{
Hans Wippel890a2cb2019-02-21 13:01:00 +0100327 if (nla_put_string(msg, SMC_PNETID_NAME, pnetelem->pnet_name))
Thomas Richter6812baa2017-01-09 16:55:15 +0100328 return -1;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100329 if (pnetelem->ndev) {
330 if (nla_put_string(msg, SMC_PNETID_ETHNAME,
331 pnetelem->ndev->name))
332 return -1;
333 } else {
334 if (nla_put_string(msg, SMC_PNETID_ETHNAME, "n/a"))
335 return -1;
336 }
337 if (pnetelem->smcibdev) {
338 if (nla_put_string(msg, SMC_PNETID_IBNAME,
339 pnetelem->smcibdev->ibdev->name) ||
340 nla_put_u8(msg, SMC_PNETID_IBPORT, pnetelem->ib_port))
341 return -1;
342 } else {
343 if (nla_put_string(msg, SMC_PNETID_IBNAME, "n/a") ||
344 nla_put_u8(msg, SMC_PNETID_IBPORT, 0xff))
345 return -1;
346 }
347
Thomas Richter6812baa2017-01-09 16:55:15 +0100348 return 0;
349}
350
Thomas Richter6812baa2017-01-09 16:55:15 +0100351static int smc_pnet_add(struct sk_buff *skb, struct genl_info *info)
352{
353 struct net *net = genl_info_net(info);
Hans Wippel890a2cb2019-02-21 13:01:00 +0100354 struct smc_user_pnetentry pnetelem;
Thomas Richter6812baa2017-01-09 16:55:15 +0100355 int rc;
356
Hans Wippel890a2cb2019-02-21 13:01:00 +0100357 rc = smc_pnet_fill_entry(net, &pnetelem, info->attrs);
Thomas Richter6812baa2017-01-09 16:55:15 +0100358 if (!rc)
Hans Wippel890a2cb2019-02-21 13:01:00 +0100359 rc = smc_pnet_enter(&pnetelem);
360 if (pnetelem.ndev)
361 dev_put(pnetelem.ndev);
Thomas Richter6812baa2017-01-09 16:55:15 +0100362 return rc;
363}
364
365static int smc_pnet_del(struct sk_buff *skb, struct genl_info *info)
366{
Eric Biggersd49baa72018-05-13 17:01:30 -0700367 if (!info->attrs[SMC_PNETID_NAME])
368 return -EINVAL;
Thomas Richter6812baa2017-01-09 16:55:15 +0100369 return smc_pnet_remove_by_pnetid(
370 (char *)nla_data(info->attrs[SMC_PNETID_NAME]));
371}
372
373static int smc_pnet_dump_start(struct netlink_callback *cb)
374{
375 cb->args[0] = 0;
376 return 0;
377}
378
379static int smc_pnet_dumpinfo(struct sk_buff *skb,
380 u32 portid, u32 seq, u32 flags,
Hans Wippel890a2cb2019-02-21 13:01:00 +0100381 struct smc_user_pnetentry *pnetelem)
Thomas Richter6812baa2017-01-09 16:55:15 +0100382{
383 void *hdr;
384
385 hdr = genlmsg_put(skb, portid, seq, &smc_pnet_nl_family,
386 flags, SMC_PNETID_GET);
387 if (!hdr)
388 return -ENOMEM;
389 if (smc_pnet_set_nla(skb, pnetelem) < 0) {
390 genlmsg_cancel(skb, hdr);
391 return -EMSGSIZE;
392 }
393 genlmsg_end(skb, hdr);
394 return 0;
395}
396
Hans Wippel890a2cb2019-02-21 13:01:00 +0100397static int _smc_pnet_dump(struct sk_buff *skb, u32 portid, u32 seq, u8 *pnetid,
398 int start_idx)
Thomas Richter6812baa2017-01-09 16:55:15 +0100399{
Hans Wippel890a2cb2019-02-21 13:01:00 +0100400 struct smc_user_pnetentry tmp_entry;
Thomas Richter6812baa2017-01-09 16:55:15 +0100401 struct smc_pnetentry *pnetelem;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100402 struct smc_ib_device *ibdev;
Thomas Richter6812baa2017-01-09 16:55:15 +0100403 int idx = 0;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100404 int ibport;
Thomas Richter6812baa2017-01-09 16:55:15 +0100405
Hans Wippel890a2cb2019-02-21 13:01:00 +0100406 /* dump netdevices */
Thomas Richter6812baa2017-01-09 16:55:15 +0100407 read_lock(&smc_pnettable.lock);
408 list_for_each_entry(pnetelem, &smc_pnettable.pnetlist, list) {
Hans Wippel890a2cb2019-02-21 13:01:00 +0100409 if (pnetid && !smc_pnet_match(pnetelem->pnet_name, pnetid))
Thomas Richter6812baa2017-01-09 16:55:15 +0100410 continue;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100411 if (idx++ < start_idx)
412 continue;
413 memset(&tmp_entry, 0, sizeof(tmp_entry));
414 memcpy(&tmp_entry.pnet_name, pnetelem->pnet_name,
415 SMC_MAX_PNETID_LEN);
416 tmp_entry.ndev = pnetelem->ndev;
417 if (smc_pnet_dumpinfo(skb, portid, seq, NLM_F_MULTI,
418 &tmp_entry)) {
Thomas Richter6812baa2017-01-09 16:55:15 +0100419 --idx;
420 break;
421 }
422 }
Thomas Richter6812baa2017-01-09 16:55:15 +0100423 read_unlock(&smc_pnettable.lock);
Hans Wippel890a2cb2019-02-21 13:01:00 +0100424
425 /* dump ib devices */
426 spin_lock(&smc_ib_devices.lock);
427 list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
428 for (ibport = 0; ibport < SMC_MAX_PORTS; ibport++) {
429 if (ibdev->pnetid_by_user[ibport]) {
430 if (pnetid &&
431 !smc_pnet_match(ibdev->pnetid[ibport],
432 pnetid))
433 continue;
434 if (idx++ < start_idx)
435 continue;
436 memset(&tmp_entry, 0, sizeof(tmp_entry));
437 memcpy(&tmp_entry.pnet_name,
438 ibdev->pnetid[ibport],
439 SMC_MAX_PNETID_LEN);
440 tmp_entry.smcibdev = ibdev;
441 tmp_entry.ib_port = ibport + 1;
442 if (smc_pnet_dumpinfo(skb, portid, seq,
443 NLM_F_MULTI,
444 &tmp_entry)) {
445 --idx;
446 break;
447 }
448 }
449 }
450 }
451 spin_unlock(&smc_ib_devices.lock);
452
453 return idx;
454}
455
456static int smc_pnet_dump(struct sk_buff *skb, struct netlink_callback *cb)
457{
458 int idx;
459
460 idx = _smc_pnet_dump(skb, NETLINK_CB(cb->skb).portid,
461 cb->nlh->nlmsg_seq, NULL, cb->args[0]);
462
463 cb->args[0] = idx;
Thomas Richter6812baa2017-01-09 16:55:15 +0100464 return skb->len;
465}
466
Hans Wippel890a2cb2019-02-21 13:01:00 +0100467/* Retrieve one PNETID entry */
468static int smc_pnet_get(struct sk_buff *skb, struct genl_info *info)
469{
470 struct sk_buff *msg;
471 void *hdr;
472
473 if (!info->attrs[SMC_PNETID_NAME])
474 return -EINVAL;
475
476 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
477 if (!msg)
478 return -ENOMEM;
479
480 _smc_pnet_dump(msg, info->snd_portid, info->snd_seq,
481 nla_data(info->attrs[SMC_PNETID_NAME]), 0);
482
483 /* finish multi part message and send it */
484 hdr = nlmsg_put(msg, info->snd_portid, info->snd_seq, NLMSG_DONE, 0,
485 NLM_F_MULTI);
486 if (!hdr) {
487 nlmsg_free(msg);
488 return -EMSGSIZE;
489 }
490 return genlmsg_reply(msg, info);
491}
492
Thomas Richter6812baa2017-01-09 16:55:15 +0100493/* Remove and delete all pnetids from pnet table.
494 */
495static int smc_pnet_flush(struct sk_buff *skb, struct genl_info *info)
496{
Hans Wippel890a2cb2019-02-21 13:01:00 +0100497 return smc_pnet_remove_by_pnetid(NULL);
Thomas Richter6812baa2017-01-09 16:55:15 +0100498}
499
500/* SMC_PNETID generic netlink operation definition */
501static const struct genl_ops smc_pnet_ops[] = {
502 {
503 .cmd = SMC_PNETID_GET,
504 .flags = GENL_ADMIN_PERM,
505 .policy = smc_pnet_policy,
506 .doit = smc_pnet_get,
507 .dumpit = smc_pnet_dump,
508 .start = smc_pnet_dump_start
509 },
510 {
511 .cmd = SMC_PNETID_ADD,
512 .flags = GENL_ADMIN_PERM,
513 .policy = smc_pnet_policy,
514 .doit = smc_pnet_add
515 },
516 {
517 .cmd = SMC_PNETID_DEL,
518 .flags = GENL_ADMIN_PERM,
519 .policy = smc_pnet_policy,
520 .doit = smc_pnet_del
521 },
522 {
523 .cmd = SMC_PNETID_FLUSH,
524 .flags = GENL_ADMIN_PERM,
525 .policy = smc_pnet_policy,
526 .doit = smc_pnet_flush
527 }
528};
529
530/* SMC_PNETID family definition */
Johannes Berg56ce3c52018-09-20 09:27:30 +0200531static struct genl_family smc_pnet_nl_family __ro_after_init = {
Thomas Richter6812baa2017-01-09 16:55:15 +0100532 .hdrsize = 0,
533 .name = SMCR_GENL_FAMILY_NAME,
534 .version = SMCR_GENL_FAMILY_VERSION,
535 .maxattr = SMC_PNETID_MAX,
536 .netnsok = true,
537 .module = THIS_MODULE,
538 .ops = smc_pnet_ops,
539 .n_ops = ARRAY_SIZE(smc_pnet_ops)
540};
541
542static int smc_pnet_netdev_event(struct notifier_block *this,
543 unsigned long event, void *ptr)
544{
545 struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
546
547 switch (event) {
548 case NETDEV_REBOOT:
549 case NETDEV_UNREGISTER:
550 smc_pnet_remove_by_ndev(event_dev);
Ursula Braunbe6a3f32018-06-28 19:05:04 +0200551 return NOTIFY_OK;
Thomas Richter6812baa2017-01-09 16:55:15 +0100552 default:
Ursula Braunbe6a3f32018-06-28 19:05:04 +0200553 return NOTIFY_DONE;
Thomas Richter6812baa2017-01-09 16:55:15 +0100554 }
Thomas Richter6812baa2017-01-09 16:55:15 +0100555}
556
557static struct notifier_block smc_netdev_notifier = {
558 .notifier_call = smc_pnet_netdev_event
559};
560
561int __init smc_pnet_init(void)
562{
563 int rc;
564
565 rc = genl_register_family(&smc_pnet_nl_family);
566 if (rc)
567 return rc;
568 rc = register_netdevice_notifier(&smc_netdev_notifier);
569 if (rc)
570 genl_unregister_family(&smc_pnet_nl_family);
571 return rc;
572}
573
574void smc_pnet_exit(void)
575{
576 smc_pnet_flush(NULL, NULL);
577 unregister_netdevice_notifier(&smc_netdev_notifier);
578 genl_unregister_family(&smc_pnet_nl_family);
579}
580
Ursula Braun0afff912018-06-28 19:05:05 +0200581/* Determine one base device for stacked net devices.
582 * If the lower device level contains more than one devices
583 * (for instance with bonding slaves), just the first device
584 * is used to reach a base device.
Thomas Richter6812baa2017-01-09 16:55:15 +0100585 */
Ursula Braun0afff912018-06-28 19:05:05 +0200586static struct net_device *pnet_find_base_ndev(struct net_device *ndev)
Thomas Richter6812baa2017-01-09 16:55:15 +0100587{
Ursula Braun0afff912018-06-28 19:05:05 +0200588 int i, nest_lvl;
589
590 rtnl_lock();
591 nest_lvl = dev_get_nest_level(ndev);
592 for (i = 0; i < nest_lvl; i++) {
593 struct list_head *lower = &ndev->adj_list.lower;
594
595 if (list_empty(lower))
596 break;
597 lower = lower->next;
598 ndev = netdev_lower_get_next(ndev, &lower);
599 }
600 rtnl_unlock();
601 return ndev;
602}
603
Hans Wippel890a2cb2019-02-21 13:01:00 +0100604static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *netdev,
605 u8 *pnetid)
606{
607 struct smc_pnetentry *pnetelem;
608 int rc = -ENOENT;
609
610 read_lock(&smc_pnettable.lock);
611 list_for_each_entry(pnetelem, &smc_pnettable.pnetlist, list) {
612 if (netdev == pnetelem->ndev) {
613 /* get pnetid of netdev device */
614 memcpy(pnetid, pnetelem->pnet_name, SMC_MAX_PNETID_LEN);
615 rc = 0;
616 break;
617 }
618 }
619 read_unlock(&smc_pnettable.lock);
620 return rc;
621}
622
Ursula Braun0afff912018-06-28 19:05:05 +0200623/* Determine the corresponding IB device port based on the hardware PNETID.
Ursula Braun7005ada2018-07-25 16:35:31 +0200624 * Searching stops at the first matching active IB device port with vlan_id
625 * configured.
Ursula Braun0afff912018-06-28 19:05:05 +0200626 */
627static void smc_pnet_find_roce_by_pnetid(struct net_device *ndev,
628 struct smc_ib_device **smcibdev,
Ursula Braun7005ada2018-07-25 16:35:31 +0200629 u8 *ibport, unsigned short vlan_id,
630 u8 gid[])
Ursula Braun0afff912018-06-28 19:05:05 +0200631{
632 u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
633 struct smc_ib_device *ibdev;
634 int i;
635
636 ndev = pnet_find_base_ndev(ndev);
637 if (smc_pnetid_by_dev_port(ndev->dev.parent, ndev->dev_port,
Hans Wippel890a2cb2019-02-21 13:01:00 +0100638 ndev_pnetid) &&
639 smc_pnet_find_ndev_pnetid_by_table(ndev, ndev_pnetid))
Ursula Braun0afff912018-06-28 19:05:05 +0200640 return; /* pnetid could not be determined */
641
642 spin_lock(&smc_ib_devices.lock);
643 list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
644 for (i = 1; i <= SMC_MAX_PORTS; i++) {
Ursula Braun7005ada2018-07-25 16:35:31 +0200645 if (!rdma_is_port_valid(ibdev->ibdev, i))
646 continue;
Hans Wippel890a2cb2019-02-21 13:01:00 +0100647 if (smc_pnet_match(ibdev->pnetid[i - 1], ndev_pnetid) &&
Ursula Braun7005ada2018-07-25 16:35:31 +0200648 smc_ib_port_active(ibdev, i) &&
649 !smc_ib_determine_gid(ibdev, i, vlan_id, gid,
650 NULL)) {
Ursula Braun0afff912018-06-28 19:05:05 +0200651 *smcibdev = ibdev;
652 *ibport = i;
Ursula Braun7005ada2018-07-25 16:35:31 +0200653 goto out;
Ursula Braun0afff912018-06-28 19:05:05 +0200654 }
655 }
656 }
Ursula Braun7005ada2018-07-25 16:35:31 +0200657out:
Ursula Braun0afff912018-06-28 19:05:05 +0200658 spin_unlock(&smc_ib_devices.lock);
659}
660
Hans Wippel1619f772018-06-28 19:05:08 +0200661static void smc_pnet_find_ism_by_pnetid(struct net_device *ndev,
662 struct smcd_dev **smcismdev)
663{
664 u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
665 struct smcd_dev *ismdev;
666
667 ndev = pnet_find_base_ndev(ndev);
668 if (smc_pnetid_by_dev_port(ndev->dev.parent, ndev->dev_port,
669 ndev_pnetid))
670 return; /* pnetid could not be determined */
671
672 spin_lock(&smcd_dev_list.lock);
673 list_for_each_entry(ismdev, &smcd_dev_list.list, list) {
674 if (!memcmp(ismdev->pnetid, ndev_pnetid, SMC_MAX_PNETID_LEN)) {
675 *smcismdev = ismdev;
676 break;
677 }
678 }
679 spin_unlock(&smcd_dev_list.lock);
680}
681
Ursula Braun0afff912018-06-28 19:05:05 +0200682/* PNET table analysis for a given sock:
683 * determine ib_device and port belonging to used internal TCP socket
684 * ethernet interface.
685 */
686void smc_pnet_find_roce_resource(struct sock *sk,
Ursula Braun7005ada2018-07-25 16:35:31 +0200687 struct smc_ib_device **smcibdev, u8 *ibport,
688 unsigned short vlan_id, u8 gid[])
Ursula Braun0afff912018-06-28 19:05:05 +0200689{
690 struct dst_entry *dst = sk_dst_get(sk);
691
692 *smcibdev = NULL;
693 *ibport = 0;
694
695 if (!dst)
696 goto out;
697 if (!dst->dev)
698 goto out_rel;
699
Ursula Braun7005ada2018-07-25 16:35:31 +0200700 smc_pnet_find_roce_by_pnetid(dst->dev, smcibdev, ibport, vlan_id, gid);
Ursula Braun0afff912018-06-28 19:05:05 +0200701
Thomas Richter6812baa2017-01-09 16:55:15 +0100702out_rel:
703 dst_release(dst);
Ursula Braun0afff912018-06-28 19:05:05 +0200704out:
705 return;
Thomas Richter6812baa2017-01-09 16:55:15 +0100706}
Hans Wippel1619f772018-06-28 19:05:08 +0200707
708void smc_pnet_find_ism_resource(struct sock *sk, struct smcd_dev **smcismdev)
709{
710 struct dst_entry *dst = sk_dst_get(sk);
711
712 *smcismdev = NULL;
713 if (!dst)
714 goto out;
715 if (!dst->dev)
716 goto out_rel;
717
718 /* if possible, lookup via hardware-defined pnetid */
719 smc_pnet_find_ism_by_pnetid(dst->dev, smcismdev);
720
721out_rel:
722 dst_release(dst);
723out:
724 return;
725}