11
11
#include <linux/in6.h>
12
12
#include <linux/mutex.h>
13
13
#include <linux/random.h>
14
+ #include <linux/rbtree.h>
14
15
#include <linux/igmp.h>
15
16
#include <linux/xarray.h>
16
17
#include <linux/inetdevice.h>
20
21
21
22
#include <net/net_namespace.h>
22
23
#include <net/netns/generic.h>
24
+ #include <net/netevent.h>
23
25
#include <net/tcp.h>
24
26
#include <net/ipv6.h>
25
27
#include <net/ip_fib.h>
@@ -168,6 +170,9 @@ static struct ib_sa_client sa_client;
168
170
static LIST_HEAD (dev_list );
169
171
static LIST_HEAD (listen_any_list );
170
172
static DEFINE_MUTEX (lock );
173
+ static struct rb_root id_table = RB_ROOT ;
174
+ /* Serialize operations of id_table tree */
175
+ static DEFINE_SPINLOCK (id_table_lock );
171
176
static struct workqueue_struct * cma_wq ;
172
177
static unsigned int cma_pernet_id ;
173
178
@@ -202,6 +207,11 @@ struct xarray *cma_pernet_xa(struct net *net, enum rdma_ucm_port_space ps)
202
207
}
203
208
}
204
209
210
+ struct id_table_entry {
211
+ struct list_head id_list ;
212
+ struct rb_node rb_node ;
213
+ };
214
+
205
215
struct cma_device {
206
216
struct list_head list ;
207
217
struct ib_device * device ;
@@ -420,11 +430,21 @@ static inline u8 cma_get_ip_ver(const struct cma_hdr *hdr)
420
430
return hdr -> ip_version >> 4 ;
421
431
}
422
432
423
- static inline void cma_set_ip_ver (struct cma_hdr * hdr , u8 ip_ver )
433
+ static void cma_set_ip_ver (struct cma_hdr * hdr , u8 ip_ver )
424
434
{
425
435
hdr -> ip_version = (ip_ver << 4 ) | (hdr -> ip_version & 0xF );
426
436
}
427
437
438
+ static struct sockaddr * cma_src_addr (struct rdma_id_private * id_priv )
439
+ {
440
+ return (struct sockaddr * )& id_priv -> id .route .addr .src_addr ;
441
+ }
442
+
443
+ static inline struct sockaddr * cma_dst_addr (struct rdma_id_private * id_priv )
444
+ {
445
+ return (struct sockaddr * )& id_priv -> id .route .addr .dst_addr ;
446
+ }
447
+
428
448
static int cma_igmp_send (struct net_device * ndev , union ib_gid * mgid , bool join )
429
449
{
430
450
struct in_device * in_dev = NULL ;
@@ -445,6 +465,117 @@ static int cma_igmp_send(struct net_device *ndev, union ib_gid *mgid, bool join)
445
465
return (in_dev ) ? 0 : - ENODEV ;
446
466
}
447
467
468
+ static int compare_netdev_and_ip (int ifindex_a , struct sockaddr * sa ,
469
+ struct id_table_entry * entry_b )
470
+ {
471
+ struct rdma_id_private * id_priv = list_first_entry (
472
+ & entry_b -> id_list , struct rdma_id_private , id_list_entry );
473
+ int ifindex_b = id_priv -> id .route .addr .dev_addr .bound_dev_if ;
474
+ struct sockaddr * sb = cma_dst_addr (id_priv );
475
+
476
+ if (ifindex_a != ifindex_b )
477
+ return (ifindex_a > ifindex_b ) ? 1 : -1 ;
478
+
479
+ if (sa -> sa_family != sb -> sa_family )
480
+ return sa -> sa_family - sb -> sa_family ;
481
+
482
+ if (sa -> sa_family == AF_INET )
483
+ return memcmp ((char * )& ((struct sockaddr_in * )sa )-> sin_addr ,
484
+ (char * )& ((struct sockaddr_in * )sb )-> sin_addr ,
485
+ sizeof (((struct sockaddr_in * )sa )-> sin_addr ));
486
+
487
+ return ipv6_addr_cmp (& ((struct sockaddr_in6 * )sa )-> sin6_addr ,
488
+ & ((struct sockaddr_in6 * )sb )-> sin6_addr );
489
+ }
490
+
491
+ static int cma_add_id_to_tree (struct rdma_id_private * node_id_priv )
492
+ {
493
+ struct rb_node * * new , * parent = NULL ;
494
+ struct id_table_entry * this , * node ;
495
+ unsigned long flags ;
496
+ int result ;
497
+
498
+ node = kzalloc (sizeof (* node ), GFP_KERNEL );
499
+ if (!node )
500
+ return - ENOMEM ;
501
+
502
+ spin_lock_irqsave (& id_table_lock , flags );
503
+ new = & id_table .rb_node ;
504
+ while (* new ) {
505
+ this = container_of (* new , struct id_table_entry , rb_node );
506
+ result = compare_netdev_and_ip (
507
+ node_id_priv -> id .route .addr .dev_addr .bound_dev_if ,
508
+ cma_dst_addr (node_id_priv ), this );
509
+
510
+ parent = * new ;
511
+ if (result < 0 )
512
+ new = & ((* new )-> rb_left );
513
+ else if (result > 0 )
514
+ new = & ((* new )-> rb_right );
515
+ else {
516
+ list_add_tail (& node_id_priv -> id_list_entry ,
517
+ & this -> id_list );
518
+ kfree (node );
519
+ goto unlock ;
520
+ }
521
+ }
522
+
523
+ INIT_LIST_HEAD (& node -> id_list );
524
+ list_add_tail (& node_id_priv -> id_list_entry , & node -> id_list );
525
+
526
+ rb_link_node (& node -> rb_node , parent , new );
527
+ rb_insert_color (& node -> rb_node , & id_table );
528
+
529
+ unlock :
530
+ spin_unlock_irqrestore (& id_table_lock , flags );
531
+ return 0 ;
532
+ }
533
+
534
+ static struct id_table_entry *
535
+ node_from_ndev_ip (struct rb_root * root , int ifindex , struct sockaddr * sa )
536
+ {
537
+ struct rb_node * node = root -> rb_node ;
538
+ struct id_table_entry * data ;
539
+ int result ;
540
+
541
+ while (node ) {
542
+ data = container_of (node , struct id_table_entry , rb_node );
543
+ result = compare_netdev_and_ip (ifindex , sa , data );
544
+ if (result < 0 )
545
+ node = node -> rb_left ;
546
+ else if (result > 0 )
547
+ node = node -> rb_right ;
548
+ else
549
+ return data ;
550
+ }
551
+
552
+ return NULL ;
553
+ }
554
+
555
+ static void cma_remove_id_from_tree (struct rdma_id_private * id_priv )
556
+ {
557
+ struct id_table_entry * data ;
558
+ unsigned long flags ;
559
+
560
+ spin_lock_irqsave (& id_table_lock , flags );
561
+ if (list_empty (& id_priv -> id_list_entry ))
562
+ goto out ;
563
+
564
+ data = node_from_ndev_ip (& id_table ,
565
+ id_priv -> id .route .addr .dev_addr .bound_dev_if ,
566
+ cma_dst_addr (id_priv ));
567
+ if (!data )
568
+ goto out ;
569
+
570
+ list_del_init (& id_priv -> id_list_entry );
571
+ if (list_empty (& data -> id_list )) {
572
+ rb_erase (& data -> rb_node , & id_table );
573
+ kfree (data );
574
+ }
575
+ out :
576
+ spin_unlock_irqrestore (& id_table_lock , flags );
577
+ }
578
+
448
579
static void _cma_attach_to_dev (struct rdma_id_private * id_priv ,
449
580
struct cma_device * cma_dev )
450
581
{
@@ -481,16 +612,6 @@ static void cma_release_dev(struct rdma_id_private *id_priv)
481
612
mutex_unlock (& lock );
482
613
}
483
614
484
- static inline struct sockaddr * cma_src_addr (struct rdma_id_private * id_priv )
485
- {
486
- return (struct sockaddr * ) & id_priv -> id .route .addr .src_addr ;
487
- }
488
-
489
- static inline struct sockaddr * cma_dst_addr (struct rdma_id_private * id_priv )
490
- {
491
- return (struct sockaddr * ) & id_priv -> id .route .addr .dst_addr ;
492
- }
493
-
494
615
static inline unsigned short cma_family (struct rdma_id_private * id_priv )
495
616
{
496
617
return id_priv -> id .route .addr .src_addr .ss_family ;
@@ -861,6 +982,7 @@ __rdma_create_id(struct net *net, rdma_cm_event_handler event_handler,
861
982
refcount_set (& id_priv -> refcount , 1 );
862
983
mutex_init (& id_priv -> handler_mutex );
863
984
INIT_LIST_HEAD (& id_priv -> device_item );
985
+ INIT_LIST_HEAD (& id_priv -> id_list_entry );
864
986
INIT_LIST_HEAD (& id_priv -> listen_list );
865
987
INIT_LIST_HEAD (& id_priv -> mc_list );
866
988
get_random_bytes (& id_priv -> seq_num , sizeof id_priv -> seq_num );
@@ -1883,6 +2005,7 @@ static void _destroy_id(struct rdma_id_private *id_priv,
1883
2005
cma_cancel_operation (id_priv , state );
1884
2006
1885
2007
rdma_restrack_del (& id_priv -> res );
2008
+ cma_remove_id_from_tree (id_priv );
1886
2009
if (id_priv -> cma_dev ) {
1887
2010
if (rdma_cap_ib_cm (id_priv -> id .device , 1 )) {
1888
2011
if (id_priv -> cm_id .ib )
@@ -3172,8 +3295,11 @@ int rdma_resolve_route(struct rdma_cm_id *id, unsigned long timeout_ms)
3172
3295
cma_id_get (id_priv );
3173
3296
if (rdma_cap_ib_sa (id -> device , id -> port_num ))
3174
3297
ret = cma_resolve_ib_route (id_priv , timeout_ms );
3175
- else if (rdma_protocol_roce (id -> device , id -> port_num ))
3298
+ else if (rdma_protocol_roce (id -> device , id -> port_num )) {
3176
3299
ret = cma_resolve_iboe_route (id_priv );
3300
+ if (!ret )
3301
+ cma_add_id_to_tree (id_priv );
3302
+ }
3177
3303
else if (rdma_protocol_iwarp (id -> device , id -> port_num ))
3178
3304
ret = cma_resolve_iw_route (id_priv );
3179
3305
else
@@ -4922,10 +5048,87 @@ static int cma_netdev_callback(struct notifier_block *self, unsigned long event,
4922
5048
return ret ;
4923
5049
}
4924
5050
5051
+ static void cma_netevent_work_handler (struct work_struct * _work )
5052
+ {
5053
+ struct rdma_id_private * id_priv =
5054
+ container_of (_work , struct rdma_id_private , id .net_work );
5055
+ struct rdma_cm_event event = {};
5056
+
5057
+ mutex_lock (& id_priv -> handler_mutex );
5058
+
5059
+ if (READ_ONCE (id_priv -> state ) == RDMA_CM_DESTROYING ||
5060
+ READ_ONCE (id_priv -> state ) == RDMA_CM_DEVICE_REMOVAL )
5061
+ goto out_unlock ;
5062
+
5063
+ event .event = RDMA_CM_EVENT_UNREACHABLE ;
5064
+ event .status = - ETIMEDOUT ;
5065
+
5066
+ if (cma_cm_event_handler (id_priv , & event )) {
5067
+ __acquire (& id_priv -> handler_mutex );
5068
+ id_priv -> cm_id .ib = NULL ;
5069
+ cma_id_put (id_priv );
5070
+ destroy_id_handler_unlock (id_priv );
5071
+ return ;
5072
+ }
5073
+
5074
+ out_unlock :
5075
+ mutex_unlock (& id_priv -> handler_mutex );
5076
+ cma_id_put (id_priv );
5077
+ }
5078
+
5079
+ static int cma_netevent_callback (struct notifier_block * self ,
5080
+ unsigned long event , void * ctx )
5081
+ {
5082
+ struct id_table_entry * ips_node = NULL ;
5083
+ struct rdma_id_private * current_id ;
5084
+ struct neighbour * neigh = ctx ;
5085
+ unsigned long flags ;
5086
+
5087
+ if (event != NETEVENT_NEIGH_UPDATE )
5088
+ return NOTIFY_DONE ;
5089
+
5090
+ spin_lock_irqsave (& id_table_lock , flags );
5091
+ if (neigh -> tbl -> family == AF_INET6 ) {
5092
+ struct sockaddr_in6 neigh_sock_6 ;
5093
+
5094
+ neigh_sock_6 .sin6_family = AF_INET6 ;
5095
+ neigh_sock_6 .sin6_addr = * (struct in6_addr * )neigh -> primary_key ;
5096
+ ips_node = node_from_ndev_ip (& id_table , neigh -> dev -> ifindex ,
5097
+ (struct sockaddr * )& neigh_sock_6 );
5098
+ } else if (neigh -> tbl -> family == AF_INET ) {
5099
+ struct sockaddr_in neigh_sock_4 ;
5100
+
5101
+ neigh_sock_4 .sin_family = AF_INET ;
5102
+ neigh_sock_4 .sin_addr .s_addr = * (__be32 * )(neigh -> primary_key );
5103
+ ips_node = node_from_ndev_ip (& id_table , neigh -> dev -> ifindex ,
5104
+ (struct sockaddr * )& neigh_sock_4 );
5105
+ } else
5106
+ goto out ;
5107
+
5108
+ if (!ips_node )
5109
+ goto out ;
5110
+
5111
+ list_for_each_entry (current_id , & ips_node -> id_list , id_list_entry ) {
5112
+ if (!memcmp (current_id -> id .route .addr .dev_addr .dst_dev_addr ,
5113
+ neigh -> ha , ETH_ALEN ))
5114
+ continue ;
5115
+ INIT_WORK (& current_id -> id .net_work , cma_netevent_work_handler );
5116
+ cma_id_get (current_id );
5117
+ queue_work (cma_wq , & current_id -> id .net_work );
5118
+ }
5119
+ out :
5120
+ spin_unlock_irqrestore (& id_table_lock , flags );
5121
+ return NOTIFY_DONE ;
5122
+ }
5123
+
4925
5124
static struct notifier_block cma_nb = {
4926
5125
.notifier_call = cma_netdev_callback
4927
5126
};
4928
5127
5128
+ static struct notifier_block cma_netevent_cb = {
5129
+ .notifier_call = cma_netevent_callback
5130
+ };
5131
+
4929
5132
static void cma_send_device_removal_put (struct rdma_id_private * id_priv )
4930
5133
{
4931
5134
struct rdma_cm_event event = { .event = RDMA_CM_EVENT_DEVICE_REMOVAL };
@@ -5148,6 +5351,7 @@ static int __init cma_init(void)
5148
5351
5149
5352
ib_sa_register_client (& sa_client );
5150
5353
register_netdevice_notifier (& cma_nb );
5354
+ register_netevent_notifier (& cma_netevent_cb );
5151
5355
5152
5356
ret = ib_register_client (& cma_client );
5153
5357
if (ret )
@@ -5162,6 +5366,7 @@ static int __init cma_init(void)
5162
5366
err_ib :
5163
5367
ib_unregister_client (& cma_client );
5164
5368
err :
5369
+ unregister_netevent_notifier (& cma_netevent_cb );
5165
5370
unregister_netdevice_notifier (& cma_nb );
5166
5371
ib_sa_unregister_client (& sa_client );
5167
5372
unregister_pernet_subsys (& cma_pernet_operations );
@@ -5174,6 +5379,7 @@ static void __exit cma_cleanup(void)
5174
5379
{
5175
5380
cma_configfs_exit ();
5176
5381
ib_unregister_client (& cma_client );
5382
+ unregister_netevent_notifier (& cma_netevent_cb );
5177
5383
unregister_netdevice_notifier (& cma_nb );
5178
5384
ib_sa_unregister_client (& sa_client );
5179
5385
unregister_pernet_subsys (& cma_pernet_operations );
0 commit comments