]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/infiniband/core/iwpm_util.c
RDMA/iwpm: fix memory leak on map_info
[mirror_ubuntu-bionic-kernel.git] / drivers / infiniband / core / iwpm_util.c
CommitLineData
30dc5e63
TN
1/*
2 * Copyright (c) 2014 Chelsio, Inc. All rights reserved.
3 * Copyright (c) 2014 Intel Corporation. All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses. You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 * Redistribution and use in source and binary forms, with or
12 * without modification, are permitted provided that the following
13 * conditions are met:
14 *
15 * - Redistributions of source code must retain the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer.
18 *
19 * - Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials
22 * provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 */
33
34#include "iwpm_util.h"
35
6eec1774
TN
36#define IWPM_MAPINFO_HASH_SIZE 512
37#define IWPM_MAPINFO_HASH_MASK (IWPM_MAPINFO_HASH_SIZE - 1)
38#define IWPM_REMINFO_HASH_SIZE 64
39#define IWPM_REMINFO_HASH_MASK (IWPM_REMINFO_HASH_SIZE - 1)
e972dabf 40#define IWPM_MSG_SIZE 512
30dc5e63
TN
41
42static LIST_HEAD(iwpm_nlmsg_req_list);
43static DEFINE_SPINLOCK(iwpm_nlmsg_req_lock);
44
45static struct hlist_head *iwpm_hash_bucket;
46static DEFINE_SPINLOCK(iwpm_mapinfo_lock);
47
6eec1774
TN
48static struct hlist_head *iwpm_reminfo_bucket;
49static DEFINE_SPINLOCK(iwpm_reminfo_lock);
50
30dc5e63
TN
51static DEFINE_MUTEX(iwpm_admin_lock);
52static struct iwpm_admin_data iwpm_admin;
53
54int iwpm_init(u8 nl_client)
55{
6eec1774 56 int ret = 0;
30dc5e63
TN
57 mutex_lock(&iwpm_admin_lock);
58 if (atomic_read(&iwpm_admin.refcount) == 0) {
6eec1774 59 iwpm_hash_bucket = kzalloc(IWPM_MAPINFO_HASH_SIZE *
30dc5e63
TN
60 sizeof(struct hlist_head), GFP_KERNEL);
61 if (!iwpm_hash_bucket) {
6eec1774 62 ret = -ENOMEM;
6eec1774
TN
63 goto init_exit;
64 }
65 iwpm_reminfo_bucket = kzalloc(IWPM_REMINFO_HASH_SIZE *
66 sizeof(struct hlist_head), GFP_KERNEL);
67 if (!iwpm_reminfo_bucket) {
68 kfree(iwpm_hash_bucket);
69 ret = -ENOMEM;
6eec1774 70 goto init_exit;
30dc5e63
TN
71 }
72 }
73 atomic_inc(&iwpm_admin.refcount);
6eec1774 74init_exit:
30dc5e63 75 mutex_unlock(&iwpm_admin_lock);
6eec1774
TN
76 if (!ret) {
77 iwpm_set_valid(nl_client, 1);
a7f2f24c 78 iwpm_set_registration(nl_client, IWPM_REG_UNDEF);
6eec1774
TN
79 pr_debug("%s: Mapinfo and reminfo tables are created\n",
80 __func__);
81 }
82 return ret;
30dc5e63 83}
30dc5e63
TN
84
85static void free_hash_bucket(void);
6eec1774 86static void free_reminfo_bucket(void);
30dc5e63
TN
87
88int iwpm_exit(u8 nl_client)
89{
90
91 if (!iwpm_valid_client(nl_client))
92 return -EINVAL;
93 mutex_lock(&iwpm_admin_lock);
94 if (atomic_read(&iwpm_admin.refcount) == 0) {
95 mutex_unlock(&iwpm_admin_lock);
96 pr_err("%s Incorrect usage - negative refcount\n", __func__);
97 return -EINVAL;
98 }
99 if (atomic_dec_and_test(&iwpm_admin.refcount)) {
100 free_hash_bucket();
6eec1774
TN
101 free_reminfo_bucket();
102 pr_debug("%s: Resources are destroyed\n", __func__);
30dc5e63
TN
103 }
104 mutex_unlock(&iwpm_admin_lock);
105 iwpm_set_valid(nl_client, 0);
a7f2f24c 106 iwpm_set_registration(nl_client, IWPM_REG_UNDEF);
30dc5e63
TN
107 return 0;
108}
30dc5e63 109
6eec1774 110static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *,
30dc5e63
TN
111 struct sockaddr_storage *);
112
113int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
114 struct sockaddr_storage *mapped_sockaddr,
115 u8 nl_client)
116{
66c8d473 117 struct hlist_head *hash_bucket_head = NULL;
30dc5e63
TN
118 struct iwpm_mapping_info *map_info;
119 unsigned long flags;
6eec1774 120 int ret = -EINVAL;
30dc5e63
TN
121
122 if (!iwpm_valid_client(nl_client))
6eec1774 123 return ret;
30dc5e63 124 map_info = kzalloc(sizeof(struct iwpm_mapping_info), GFP_KERNEL);
a0b3455f 125 if (!map_info)
30dc5e63 126 return -ENOMEM;
a0b3455f 127
30dc5e63
TN
128 memcpy(&map_info->local_sockaddr, local_sockaddr,
129 sizeof(struct sockaddr_storage));
130 memcpy(&map_info->mapped_sockaddr, mapped_sockaddr,
131 sizeof(struct sockaddr_storage));
132 map_info->nl_client = nl_client;
133
134 spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
135 if (iwpm_hash_bucket) {
6eec1774 136 hash_bucket_head = get_mapinfo_hash_bucket(
30dc5e63
TN
137 &map_info->local_sockaddr,
138 &map_info->mapped_sockaddr);
6eec1774
TN
139 if (hash_bucket_head) {
140 hlist_add_head(&map_info->hlist_node, hash_bucket_head);
141 ret = 0;
142 }
30dc5e63
TN
143 }
144 spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
66c8d473
CIK
145
146 if (!hash_bucket_head)
147 kfree(map_info);
6eec1774 148 return ret;
30dc5e63 149}
30dc5e63
TN
150
151int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr,
152 struct sockaddr_storage *mapped_local_addr)
153{
154 struct hlist_node *tmp_hlist_node;
155 struct hlist_head *hash_bucket_head;
156 struct iwpm_mapping_info *map_info = NULL;
157 unsigned long flags;
158 int ret = -EINVAL;
159
160 spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
161 if (iwpm_hash_bucket) {
6eec1774 162 hash_bucket_head = get_mapinfo_hash_bucket(
30dc5e63
TN
163 local_sockaddr,
164 mapped_local_addr);
6eec1774
TN
165 if (!hash_bucket_head)
166 goto remove_mapinfo_exit;
167
30dc5e63
TN
168 hlist_for_each_entry_safe(map_info, tmp_hlist_node,
169 hash_bucket_head, hlist_node) {
170
171 if (!iwpm_compare_sockaddr(&map_info->mapped_sockaddr,
172 mapped_local_addr)) {
173
174 hlist_del_init(&map_info->hlist_node);
175 kfree(map_info);
176 ret = 0;
177 break;
178 }
179 }
180 }
6eec1774 181remove_mapinfo_exit:
30dc5e63
TN
182 spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
183 return ret;
184}
30dc5e63
TN
185
186static void free_hash_bucket(void)
187{
188 struct hlist_node *tmp_hlist_node;
189 struct iwpm_mapping_info *map_info;
190 unsigned long flags;
191 int i;
192
193 /* remove all the mapinfo data from the list */
194 spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
6eec1774 195 for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
30dc5e63
TN
196 hlist_for_each_entry_safe(map_info, tmp_hlist_node,
197 &iwpm_hash_bucket[i], hlist_node) {
198
199 hlist_del_init(&map_info->hlist_node);
200 kfree(map_info);
201 }
202 }
203 /* free the hash list */
204 kfree(iwpm_hash_bucket);
205 iwpm_hash_bucket = NULL;
206 spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
207}
208
6eec1774
TN
209static void free_reminfo_bucket(void)
210{
211 struct hlist_node *tmp_hlist_node;
212 struct iwpm_remote_info *rem_info;
213 unsigned long flags;
214 int i;
215
216 /* remove all the remote info from the list */
217 spin_lock_irqsave(&iwpm_reminfo_lock, flags);
218 for (i = 0; i < IWPM_REMINFO_HASH_SIZE; i++) {
219 hlist_for_each_entry_safe(rem_info, tmp_hlist_node,
220 &iwpm_reminfo_bucket[i], hlist_node) {
221
222 hlist_del_init(&rem_info->hlist_node);
223 kfree(rem_info);
224 }
225 }
226 /* free the hash list */
227 kfree(iwpm_reminfo_bucket);
228 iwpm_reminfo_bucket = NULL;
229 spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
230}
231
232static struct hlist_head *get_reminfo_hash_bucket(struct sockaddr_storage *,
233 struct sockaddr_storage *);
234
235void iwpm_add_remote_info(struct iwpm_remote_info *rem_info)
236{
237 struct hlist_head *hash_bucket_head;
238 unsigned long flags;
239
240 spin_lock_irqsave(&iwpm_reminfo_lock, flags);
241 if (iwpm_reminfo_bucket) {
242 hash_bucket_head = get_reminfo_hash_bucket(
243 &rem_info->mapped_loc_sockaddr,
244 &rem_info->mapped_rem_sockaddr);
245 if (hash_bucket_head)
246 hlist_add_head(&rem_info->hlist_node, hash_bucket_head);
247 }
248 spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
249}
250
251int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr,
dafb5587
FL
252 struct sockaddr_storage *mapped_rem_addr,
253 struct sockaddr_storage *remote_addr,
254 u8 nl_client)
6eec1774
TN
255{
256 struct hlist_node *tmp_hlist_node;
257 struct hlist_head *hash_bucket_head;
258 struct iwpm_remote_info *rem_info = NULL;
259 unsigned long flags;
260 int ret = -EINVAL;
261
262 if (!iwpm_valid_client(nl_client)) {
263 pr_info("%s: Invalid client = %d\n", __func__, nl_client);
264 return ret;
265 }
266 spin_lock_irqsave(&iwpm_reminfo_lock, flags);
267 if (iwpm_reminfo_bucket) {
268 hash_bucket_head = get_reminfo_hash_bucket(
269 mapped_loc_addr,
270 mapped_rem_addr);
271 if (!hash_bucket_head)
272 goto get_remote_info_exit;
273 hlist_for_each_entry_safe(rem_info, tmp_hlist_node,
274 hash_bucket_head, hlist_node) {
275
276 if (!iwpm_compare_sockaddr(&rem_info->mapped_loc_sockaddr,
277 mapped_loc_addr) &&
278 !iwpm_compare_sockaddr(&rem_info->mapped_rem_sockaddr,
279 mapped_rem_addr)) {
280
281 memcpy(remote_addr, &rem_info->remote_sockaddr,
282 sizeof(struct sockaddr_storage));
283 iwpm_print_sockaddr(remote_addr,
284 "get_remote_info: Remote sockaddr:");
285
286 hlist_del_init(&rem_info->hlist_node);
287 kfree(rem_info);
288 ret = 0;
289 break;
290 }
291 }
292 }
293get_remote_info_exit:
294 spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
295 return ret;
296}
6eec1774 297
30dc5e63
TN
298struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq,
299 u8 nl_client, gfp_t gfp)
300{
301 struct iwpm_nlmsg_request *nlmsg_request = NULL;
302 unsigned long flags;
303
304 nlmsg_request = kzalloc(sizeof(struct iwpm_nlmsg_request), gfp);
a0b3455f 305 if (!nlmsg_request)
30dc5e63 306 return NULL;
a0b3455f 307
30dc5e63
TN
308 spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags);
309 list_add_tail(&nlmsg_request->inprocess_list, &iwpm_nlmsg_req_list);
310 spin_unlock_irqrestore(&iwpm_nlmsg_req_lock, flags);
311
312 kref_init(&nlmsg_request->kref);
313 kref_get(&nlmsg_request->kref);
314 nlmsg_request->nlmsg_seq = nlmsg_seq;
315 nlmsg_request->nl_client = nl_client;
316 nlmsg_request->request_done = 0;
317 nlmsg_request->err_code = 0;
dafb5587
FL
318 sema_init(&nlmsg_request->sem, 1);
319 down(&nlmsg_request->sem);
30dc5e63
TN
320 return nlmsg_request;
321}
322
323void iwpm_free_nlmsg_request(struct kref *kref)
324{
325 struct iwpm_nlmsg_request *nlmsg_request;
326 unsigned long flags;
327
328 nlmsg_request = container_of(kref, struct iwpm_nlmsg_request, kref);
329
330 spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags);
331 list_del_init(&nlmsg_request->inprocess_list);
332 spin_unlock_irqrestore(&iwpm_nlmsg_req_lock, flags);
333
334 if (!nlmsg_request->request_done)
335 pr_debug("%s Freeing incomplete nlmsg request (seq = %u).\n",
336 __func__, nlmsg_request->nlmsg_seq);
337 kfree(nlmsg_request);
338}
339
340struct iwpm_nlmsg_request *iwpm_find_nlmsg_request(__u32 echo_seq)
341{
342 struct iwpm_nlmsg_request *nlmsg_request;
343 struct iwpm_nlmsg_request *found_request = NULL;
344 unsigned long flags;
345
346 spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags);
347 list_for_each_entry(nlmsg_request, &iwpm_nlmsg_req_list,
348 inprocess_list) {
349 if (nlmsg_request->nlmsg_seq == echo_seq) {
350 found_request = nlmsg_request;
351 kref_get(&nlmsg_request->kref);
352 break;
353 }
354 }
355 spin_unlock_irqrestore(&iwpm_nlmsg_req_lock, flags);
356 return found_request;
357}
358
359int iwpm_wait_complete_req(struct iwpm_nlmsg_request *nlmsg_request)
360{
361 int ret;
30dc5e63 362
dafb5587
FL
363 ret = down_timeout(&nlmsg_request->sem, IWPM_NL_TIMEOUT);
364 if (ret) {
30dc5e63
TN
365 ret = -EINVAL;
366 pr_info("%s: Timeout %d sec for netlink request (seq = %u)\n",
367 __func__, (IWPM_NL_TIMEOUT/HZ), nlmsg_request->nlmsg_seq);
368 } else {
369 ret = nlmsg_request->err_code;
370 }
371 kref_put(&nlmsg_request->kref, iwpm_free_nlmsg_request);
372 return ret;
373}
374
375int iwpm_get_nlmsg_seq(void)
376{
377 return atomic_inc_return(&iwpm_admin.nlmsg_seq);
378}
379
380int iwpm_valid_client(u8 nl_client)
381{
30dc5e63
TN
382 return iwpm_admin.client_list[nl_client];
383}
384
385void iwpm_set_valid(u8 nl_client, int valid)
386{
30dc5e63
TN
387 iwpm_admin.client_list[nl_client] = valid;
388}
389
390/* valid client */
a7f2f24c 391u32 iwpm_get_registration(u8 nl_client)
30dc5e63
TN
392{
393 return iwpm_admin.reg_list[nl_client];
394}
395
396/* valid client */
a7f2f24c 397void iwpm_set_registration(u8 nl_client, u32 reg)
30dc5e63
TN
398{
399 iwpm_admin.reg_list[nl_client] = reg;
400}
401
a7f2f24c
TN
402/* valid client */
403u32 iwpm_check_registration(u8 nl_client, u32 reg)
404{
405 return (iwpm_get_registration(nl_client) & reg);
406}
407
30dc5e63
TN
408int iwpm_compare_sockaddr(struct sockaddr_storage *a_sockaddr,
409 struct sockaddr_storage *b_sockaddr)
410{
411 if (a_sockaddr->ss_family != b_sockaddr->ss_family)
412 return 1;
413 if (a_sockaddr->ss_family == AF_INET) {
414 struct sockaddr_in *a4_sockaddr =
415 (struct sockaddr_in *)a_sockaddr;
416 struct sockaddr_in *b4_sockaddr =
417 (struct sockaddr_in *)b_sockaddr;
418 if (!memcmp(&a4_sockaddr->sin_addr,
419 &b4_sockaddr->sin_addr, sizeof(struct in_addr))
420 && a4_sockaddr->sin_port == b4_sockaddr->sin_port)
421 return 0;
422
423 } else if (a_sockaddr->ss_family == AF_INET6) {
424 struct sockaddr_in6 *a6_sockaddr =
425 (struct sockaddr_in6 *)a_sockaddr;
426 struct sockaddr_in6 *b6_sockaddr =
427 (struct sockaddr_in6 *)b_sockaddr;
428 if (!memcmp(&a6_sockaddr->sin6_addr,
429 &b6_sockaddr->sin6_addr, sizeof(struct in6_addr))
430 && a6_sockaddr->sin6_port == b6_sockaddr->sin6_port)
431 return 0;
432
433 } else {
434 pr_err("%s: Invalid sockaddr family\n", __func__);
435 }
436 return 1;
437}
438
439struct sk_buff *iwpm_create_nlmsg(u32 nl_op, struct nlmsghdr **nlh,
440 int nl_client)
441{
442 struct sk_buff *skb = NULL;
443
e972dabf 444 skb = dev_alloc_skb(IWPM_MSG_SIZE);
30dc5e63
TN
445 if (!skb) {
446 pr_err("%s Unable to allocate skb\n", __func__);
447 goto create_nlmsg_exit;
448 }
449 if (!(ibnl_put_msg(skb, nlh, 0, 0, nl_client, nl_op,
450 NLM_F_REQUEST))) {
451 pr_warn("%s: Unable to put the nlmsg header\n", __func__);
452 dev_kfree_skb(skb);
453 skb = NULL;
454 }
455create_nlmsg_exit:
456 return skb;
457}
458
459int iwpm_parse_nlmsg(struct netlink_callback *cb, int policy_max,
460 const struct nla_policy *nlmsg_policy,
461 struct nlattr *nltb[], const char *msg_type)
462{
463 int nlh_len = 0;
464 int ret;
465 const char *err_str = "";
466
fceb6435
JB
467 ret = nlmsg_validate(cb->nlh, nlh_len, policy_max - 1, nlmsg_policy,
468 NULL);
30dc5e63
TN
469 if (ret) {
470 err_str = "Invalid attribute";
471 goto parse_nlmsg_error;
472 }
fceb6435
JB
473 ret = nlmsg_parse(cb->nlh, nlh_len, nltb, policy_max - 1,
474 nlmsg_policy, NULL);
30dc5e63
TN
475 if (ret) {
476 err_str = "Unable to parse the nlmsg";
477 goto parse_nlmsg_error;
478 }
479 ret = iwpm_validate_nlmsg_attr(nltb, policy_max);
480 if (ret) {
481 err_str = "Invalid NULL attribute";
482 goto parse_nlmsg_error;
483 }
484 return 0;
485parse_nlmsg_error:
486 pr_warn("%s: %s (msg type %s ret = %d)\n",
487 __func__, err_str, msg_type, ret);
488 return ret;
489}
490
491void iwpm_print_sockaddr(struct sockaddr_storage *sockaddr, char *msg)
492{
493 struct sockaddr_in6 *sockaddr_v6;
494 struct sockaddr_in *sockaddr_v4;
495
496 switch (sockaddr->ss_family) {
497 case AF_INET:
498 sockaddr_v4 = (struct sockaddr_in *)sockaddr;
499 pr_debug("%s IPV4 %pI4: %u(0x%04X)\n",
500 msg, &sockaddr_v4->sin_addr,
501 ntohs(sockaddr_v4->sin_port),
502 ntohs(sockaddr_v4->sin_port));
503 break;
504 case AF_INET6:
505 sockaddr_v6 = (struct sockaddr_in6 *)sockaddr;
506 pr_debug("%s IPV6 %pI6: %u(0x%04X)\n",
507 msg, &sockaddr_v6->sin6_addr,
508 ntohs(sockaddr_v6->sin6_port),
509 ntohs(sockaddr_v6->sin6_port));
510 break;
511 default:
512 break;
513 }
514}
515
516static u32 iwpm_ipv6_jhash(struct sockaddr_in6 *ipv6_sockaddr)
517{
518 u32 ipv6_hash = jhash(&ipv6_sockaddr->sin6_addr, sizeof(struct in6_addr), 0);
519 u32 hash = jhash_2words(ipv6_hash, (__force u32) ipv6_sockaddr->sin6_port, 0);
520 return hash;
521}
522
523static u32 iwpm_ipv4_jhash(struct sockaddr_in *ipv4_sockaddr)
524{
525 u32 ipv4_hash = jhash(&ipv4_sockaddr->sin_addr, sizeof(struct in_addr), 0);
526 u32 hash = jhash_2words(ipv4_hash, (__force u32) ipv4_sockaddr->sin_port, 0);
527 return hash;
528}
529
6eec1774
TN
530static int get_hash_bucket(struct sockaddr_storage *a_sockaddr,
531 struct sockaddr_storage *b_sockaddr, u32 *hash)
30dc5e63 532{
6eec1774 533 u32 a_hash, b_hash;
30dc5e63 534
6eec1774
TN
535 if (a_sockaddr->ss_family == AF_INET) {
536 a_hash = iwpm_ipv4_jhash((struct sockaddr_in *) a_sockaddr);
537 b_hash = iwpm_ipv4_jhash((struct sockaddr_in *) b_sockaddr);
30dc5e63 538
6eec1774
TN
539 } else if (a_sockaddr->ss_family == AF_INET6) {
540 a_hash = iwpm_ipv6_jhash((struct sockaddr_in6 *) a_sockaddr);
541 b_hash = iwpm_ipv6_jhash((struct sockaddr_in6 *) b_sockaddr);
30dc5e63
TN
542 } else {
543 pr_err("%s: Invalid sockaddr family\n", __func__);
6eec1774 544 return -EINVAL;
30dc5e63
TN
545 }
546
6eec1774
TN
547 if (a_hash == b_hash) /* if port mapper isn't available */
548 *hash = a_hash;
30dc5e63 549 else
6eec1774
TN
550 *hash = jhash_2words(a_hash, b_hash, 0);
551 return 0;
552}
553
554static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage
555 *local_sockaddr, struct sockaddr_storage
556 *mapped_sockaddr)
557{
558 u32 hash;
559 int ret;
30dc5e63 560
6eec1774
TN
561 ret = get_hash_bucket(local_sockaddr, mapped_sockaddr, &hash);
562 if (ret)
563 return NULL;
564 return &iwpm_hash_bucket[hash & IWPM_MAPINFO_HASH_MASK];
565}
566
567static struct hlist_head *get_reminfo_hash_bucket(struct sockaddr_storage
568 *mapped_loc_sockaddr, struct sockaddr_storage
569 *mapped_rem_sockaddr)
570{
571 u32 hash;
572 int ret;
573
574 ret = get_hash_bucket(mapped_loc_sockaddr, mapped_rem_sockaddr, &hash);
575 if (ret)
576 return NULL;
577 return &iwpm_reminfo_bucket[hash & IWPM_REMINFO_HASH_MASK];
30dc5e63
TN
578}
579
580static int send_mapinfo_num(u32 mapping_num, u8 nl_client, int iwpm_pid)
581{
582 struct sk_buff *skb = NULL;
583 struct nlmsghdr *nlh;
584 u32 msg_seq;
585 const char *err_str = "";
586 int ret = -EINVAL;
587
588 skb = iwpm_create_nlmsg(RDMA_NL_IWPM_MAPINFO_NUM, &nlh, nl_client);
589 if (!skb) {
590 err_str = "Unable to create a nlmsg";
591 goto mapinfo_num_error;
592 }
593 nlh->nlmsg_seq = iwpm_get_nlmsg_seq();
594 msg_seq = 0;
595 err_str = "Unable to put attribute of mapinfo number nlmsg";
596 ret = ibnl_put_attr(skb, nlh, sizeof(u32), &msg_seq, IWPM_NLA_MAPINFO_SEQ);
597 if (ret)
598 goto mapinfo_num_error;
599 ret = ibnl_put_attr(skb, nlh, sizeof(u32),
600 &mapping_num, IWPM_NLA_MAPINFO_SEND_NUM);
601 if (ret)
602 goto mapinfo_num_error;
04eae427
SS
603
604 nlmsg_end(skb, nlh);
605
f00e6463 606 ret = rdma_nl_unicast(skb, iwpm_pid);
30dc5e63
TN
607 if (ret) {
608 skb = NULL;
609 err_str = "Unable to send a nlmsg";
610 goto mapinfo_num_error;
611 }
612 pr_debug("%s: Sent mapping number = %d\n", __func__, mapping_num);
613 return 0;
614mapinfo_num_error:
615 pr_info("%s: %s\n", __func__, err_str);
616 if (skb)
617 dev_kfree_skb(skb);
618 return ret;
619}
620
621static int send_nlmsg_done(struct sk_buff *skb, u8 nl_client, int iwpm_pid)
622{
623 struct nlmsghdr *nlh = NULL;
624 int ret = 0;
625
626 if (!skb)
627 return ret;
628 if (!(ibnl_put_msg(skb, &nlh, 0, 0, nl_client,
629 RDMA_NL_IWPM_MAPINFO, NLM_F_MULTI))) {
630 pr_warn("%s Unable to put NLMSG_DONE\n", __func__);
5ed935e8 631 dev_kfree_skb(skb);
30dc5e63
TN
632 return -ENOMEM;
633 }
634 nlh->nlmsg_type = NLMSG_DONE;
f00e6463 635 ret = rdma_nl_unicast(skb, iwpm_pid);
30dc5e63
TN
636 if (ret)
637 pr_warn("%s Unable to send a nlmsg\n", __func__);
638 return ret;
639}
640
641int iwpm_send_mapinfo(u8 nl_client, int iwpm_pid)
642{
643 struct iwpm_mapping_info *map_info;
644 struct sk_buff *skb = NULL;
645 struct nlmsghdr *nlh;
646 int skb_num = 0, mapping_num = 0;
647 int i = 0, nlmsg_bytes = 0;
648 unsigned long flags;
649 const char *err_str = "";
650 int ret;
651
652 skb = dev_alloc_skb(NLMSG_GOODSIZE);
653 if (!skb) {
654 ret = -ENOMEM;
655 err_str = "Unable to allocate skb";
656 goto send_mapping_info_exit;
657 }
658 skb_num++;
659 spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
eb6c9d63 660 ret = -EINVAL;
6eec1774 661 for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
30dc5e63
TN
662 hlist_for_each_entry(map_info, &iwpm_hash_bucket[i],
663 hlist_node) {
664 if (map_info->nl_client != nl_client)
665 continue;
666 nlh = NULL;
667 if (!(ibnl_put_msg(skb, &nlh, 0, 0, nl_client,
668 RDMA_NL_IWPM_MAPINFO, NLM_F_MULTI))) {
669 ret = -ENOMEM;
670 err_str = "Unable to put the nlmsg header";
671 goto send_mapping_info_unlock;
672 }
673 err_str = "Unable to put attribute of the nlmsg";
674 ret = ibnl_put_attr(skb, nlh,
675 sizeof(struct sockaddr_storage),
676 &map_info->local_sockaddr,
677 IWPM_NLA_MAPINFO_LOCAL_ADDR);
678 if (ret)
679 goto send_mapping_info_unlock;
680
681 ret = ibnl_put_attr(skb, nlh,
682 sizeof(struct sockaddr_storage),
683 &map_info->mapped_sockaddr,
684 IWPM_NLA_MAPINFO_MAPPED_ADDR);
685 if (ret)
686 goto send_mapping_info_unlock;
687
04eae427
SS
688 nlmsg_end(skb, nlh);
689
30dc5e63
TN
690 iwpm_print_sockaddr(&map_info->local_sockaddr,
691 "send_mapping_info: Local sockaddr:");
692 iwpm_print_sockaddr(&map_info->mapped_sockaddr,
693 "send_mapping_info: Mapped local sockaddr:");
694 mapping_num++;
695 nlmsg_bytes += nlh->nlmsg_len;
696
697 /* check if all mappings can fit in one skb */
698 if (NLMSG_GOODSIZE - nlmsg_bytes < nlh->nlmsg_len * 2) {
699 /* and leave room for NLMSG_DONE */
700 nlmsg_bytes = 0;
701 skb_num++;
702 spin_unlock_irqrestore(&iwpm_mapinfo_lock,
703 flags);
704 /* send the skb */
705 ret = send_nlmsg_done(skb, nl_client, iwpm_pid);
706 skb = NULL;
707 if (ret) {
708 err_str = "Unable to send map info";
709 goto send_mapping_info_exit;
710 }
711 if (skb_num == IWPM_MAPINFO_SKB_COUNT) {
712 ret = -ENOMEM;
713 err_str = "Insufficient skbs for map info";
714 goto send_mapping_info_exit;
715 }
716 skb = dev_alloc_skb(NLMSG_GOODSIZE);
717 if (!skb) {
718 ret = -ENOMEM;
719 err_str = "Unable to allocate skb";
720 goto send_mapping_info_exit;
721 }
722 spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
723 }
724 }
725 }
726send_mapping_info_unlock:
727 spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
728send_mapping_info_exit:
729 if (ret) {
730 pr_warn("%s: %s (ret = %d)\n", __func__, err_str, ret);
731 if (skb)
732 dev_kfree_skb(skb);
733 return ret;
734 }
735 send_nlmsg_done(skb, nl_client, iwpm_pid);
736 return send_mapinfo_num(mapping_num, nl_client, iwpm_pid);
737}
738
739int iwpm_mapinfo_available(void)
740{
741 unsigned long flags;
742 int full_bucket = 0, i = 0;
743
744 spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
745 if (iwpm_hash_bucket) {
6eec1774 746 for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
30dc5e63
TN
747 if (!hlist_empty(&iwpm_hash_bucket[i])) {
748 full_bucket = 1;
749 break;
750 }
751 }
752 }
753 spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
754 return full_bucket;
755}