.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
---|
2 | 2 | /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ |
---|
3 | 3 | |
---|
| 4 | +#include <linux/mutex.h> |
---|
4 | 5 | #include <linux/rhashtable.h> |
---|
5 | 6 | #include <net/ipv6.h> |
---|
6 | 7 | |
---|
.. | .. |
---|
12 | 13 | void *catchall_route_priv; |
---|
13 | 14 | struct delayed_work stats_update_dw; |
---|
14 | 15 | struct list_head table_list; |
---|
| 16 | + struct mutex table_list_lock; /* Protects table_list */ |
---|
15 | 17 | #define MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL 5000 /* ms */ |
---|
16 | 18 | unsigned long priv[0]; |
---|
17 | 19 | /* priv has to be always the last item */ |
---|
.. | .. |
---|
66 | 68 | u32 vr_id; |
---|
67 | 69 | struct mlxsw_sp_mr_vif vifs[MAXVIFS]; |
---|
68 | 70 | struct list_head route_list; |
---|
| 71 | + struct mutex route_list_lock; /* Protects route_list */ |
---|
69 | 72 | struct rhashtable route_ht; |
---|
70 | 73 | const struct mlxsw_sp_mr_table_ops *ops; |
---|
71 | | - char catchall_route_priv[0]; |
---|
| 74 | + char catchall_route_priv[]; |
---|
72 | 75 | /* catchall_route_priv has to be always the last item */ |
---|
73 | 76 | }; |
---|
74 | 77 | |
---|
.. | .. |
---|
370 | 373 | static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table, |
---|
371 | 374 | struct mlxsw_sp_mr_route *mr_route) |
---|
372 | 375 | { |
---|
| 376 | + WARN_ON_ONCE(!mutex_is_locked(&mr_table->route_list_lock)); |
---|
| 377 | + |
---|
373 | 378 | mlxsw_sp_mr_mfc_offload_set(mr_route, false); |
---|
374 | | - mlxsw_sp_mr_route_erase(mr_table, mr_route); |
---|
375 | 379 | rhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node, |
---|
376 | 380 | mlxsw_sp_mr_route_ht_params); |
---|
377 | 381 | list_del(&mr_route->node); |
---|
| 382 | + mlxsw_sp_mr_route_erase(mr_table, mr_route); |
---|
378 | 383 | mlxsw_sp_mr_route_destroy(mr_table, mr_route); |
---|
379 | 384 | } |
---|
380 | 385 | |
---|
.. | .. |
---|
415 | 420 | goto err_duplicate_route; |
---|
416 | 421 | } |
---|
417 | 422 | |
---|
| 423 | + /* Write the route to the hardware */ |
---|
| 424 | + err = mlxsw_sp_mr_route_write(mr_table, mr_route, replace); |
---|
| 425 | + if (err) |
---|
| 426 | + goto err_mr_route_write; |
---|
| 427 | + |
---|
418 | 428 | /* Put it in the table data-structures */ |
---|
| 429 | + mutex_lock(&mr_table->route_list_lock); |
---|
419 | 430 | list_add_tail(&mr_route->node, &mr_table->route_list); |
---|
| 431 | + mutex_unlock(&mr_table->route_list_lock); |
---|
420 | 432 | err = rhashtable_insert_fast(&mr_table->route_ht, |
---|
421 | 433 | &mr_route->ht_node, |
---|
422 | 434 | mlxsw_sp_mr_route_ht_params); |
---|
423 | 435 | if (err) |
---|
424 | 436 | goto err_rhashtable_insert; |
---|
425 | | - |
---|
426 | | - /* Write the route to the hardware */ |
---|
427 | | - err = mlxsw_sp_mr_route_write(mr_table, mr_route, replace); |
---|
428 | | - if (err) |
---|
429 | | - goto err_mr_route_write; |
---|
430 | 437 | |
---|
431 | 438 | /* Destroy the original route */ |
---|
432 | 439 | if (replace) { |
---|
.. | .. |
---|
440 | 447 | mlxsw_sp_mr_mfc_offload_update(mr_route); |
---|
441 | 448 | return 0; |
---|
442 | 449 | |
---|
443 | | -err_mr_route_write: |
---|
444 | | - rhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node, |
---|
445 | | - mlxsw_sp_mr_route_ht_params); |
---|
446 | 450 | err_rhashtable_insert: |
---|
| 451 | + mutex_lock(&mr_table->route_list_lock); |
---|
447 | 452 | list_del(&mr_route->node); |
---|
| 453 | + mutex_unlock(&mr_table->route_list_lock); |
---|
| 454 | + mlxsw_sp_mr_route_erase(mr_table, mr_route); |
---|
| 455 | +err_mr_route_write: |
---|
448 | 456 | err_no_orig_route: |
---|
449 | 457 | err_duplicate_route: |
---|
450 | 458 | mlxsw_sp_mr_route_destroy(mr_table, mr_route); |
---|
.. | .. |
---|
460 | 468 | mr_table->ops->key_create(mr_table, &key, mfc); |
---|
461 | 469 | mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key, |
---|
462 | 470 | mlxsw_sp_mr_route_ht_params); |
---|
463 | | - if (mr_route) |
---|
| 471 | + if (mr_route) { |
---|
| 472 | + mutex_lock(&mr_table->route_list_lock); |
---|
464 | 473 | __mlxsw_sp_mr_route_del(mr_table, mr_route); |
---|
| 474 | + mutex_unlock(&mr_table->route_list_lock); |
---|
| 475 | + } |
---|
465 | 476 | } |
---|
466 | 477 | |
---|
467 | 478 | /* Should be called after the VIF struct is updated */ |
---|
.. | .. |
---|
910 | 921 | mr_table->proto = proto; |
---|
911 | 922 | mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto]; |
---|
912 | 923 | INIT_LIST_HEAD(&mr_table->route_list); |
---|
| 924 | + mutex_init(&mr_table->route_list_lock); |
---|
913 | 925 | |
---|
914 | 926 | err = rhashtable_init(&mr_table->route_ht, |
---|
915 | 927 | &mlxsw_sp_mr_route_ht_params); |
---|
.. | .. |
---|
927 | 939 | &catchall_route_params); |
---|
928 | 940 | if (err) |
---|
929 | 941 | goto err_ops_route_create; |
---|
| 942 | + mutex_lock(&mr->table_list_lock); |
---|
930 | 943 | list_add_tail(&mr_table->node, &mr->table_list); |
---|
| 944 | + mutex_unlock(&mr->table_list_lock); |
---|
931 | 945 | return mr_table; |
---|
932 | 946 | |
---|
933 | 947 | err_ops_route_create: |
---|
934 | 948 | rhashtable_destroy(&mr_table->route_ht); |
---|
935 | 949 | err_route_rhashtable_init: |
---|
| 950 | + mutex_destroy(&mr_table->route_list_lock); |
---|
936 | 951 | kfree(mr_table); |
---|
937 | 952 | return ERR_PTR(err); |
---|
938 | 953 | } |
---|
.. | .. |
---|
943 | 958 | struct mlxsw_sp_mr *mr = mlxsw_sp->mr; |
---|
944 | 959 | |
---|
945 | 960 | WARN_ON(!mlxsw_sp_mr_table_empty(mr_table)); |
---|
| 961 | + mutex_lock(&mr->table_list_lock); |
---|
946 | 962 | list_del(&mr_table->node); |
---|
| 963 | + mutex_unlock(&mr->table_list_lock); |
---|
947 | 964 | mr->mr_ops->route_destroy(mlxsw_sp, mr->priv, |
---|
948 | 965 | &mr_table->catchall_route_priv); |
---|
949 | 966 | rhashtable_destroy(&mr_table->route_ht); |
---|
| 967 | + mutex_destroy(&mr_table->route_list_lock); |
---|
950 | 968 | kfree(mr_table); |
---|
951 | 969 | } |
---|
952 | 970 | |
---|
.. | .. |
---|
955 | 973 | struct mlxsw_sp_mr_route *mr_route, *tmp; |
---|
956 | 974 | int i; |
---|
957 | 975 | |
---|
| 976 | + mutex_lock(&mr_table->route_list_lock); |
---|
958 | 977 | list_for_each_entry_safe(mr_route, tmp, &mr_table->route_list, node) |
---|
959 | 978 | __mlxsw_sp_mr_route_del(mr_table, mr_route); |
---|
| 979 | + mutex_unlock(&mr_table->route_list_lock); |
---|
960 | 980 | |
---|
961 | 981 | for (i = 0; i < MAXVIFS; i++) { |
---|
962 | 982 | mr_table->vifs[i].dev = NULL; |
---|
.. | .. |
---|
1000 | 1020 | struct mlxsw_sp_mr_route *mr_route; |
---|
1001 | 1021 | unsigned long interval; |
---|
1002 | 1022 | |
---|
1003 | | - rtnl_lock(); |
---|
1004 | | - list_for_each_entry(mr_table, &mr->table_list, node) |
---|
| 1023 | + mutex_lock(&mr->table_list_lock); |
---|
| 1024 | + list_for_each_entry(mr_table, &mr->table_list, node) { |
---|
| 1025 | + mutex_lock(&mr_table->route_list_lock); |
---|
1005 | 1026 | list_for_each_entry(mr_route, &mr_table->route_list, node) |
---|
1006 | 1027 | mlxsw_sp_mr_route_stats_update(mr_table->mlxsw_sp, |
---|
1007 | 1028 | mr_route); |
---|
1008 | | - rtnl_unlock(); |
---|
| 1029 | + mutex_unlock(&mr_table->route_list_lock); |
---|
| 1030 | + } |
---|
| 1031 | + mutex_unlock(&mr->table_list_lock); |
---|
1009 | 1032 | |
---|
1010 | 1033 | interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL); |
---|
1011 | 1034 | mlxsw_core_schedule_dw(&mr->stats_update_dw, interval); |
---|
.. | .. |
---|
1024 | 1047 | mr->mr_ops = mr_ops; |
---|
1025 | 1048 | mlxsw_sp->mr = mr; |
---|
1026 | 1049 | INIT_LIST_HEAD(&mr->table_list); |
---|
| 1050 | + mutex_init(&mr->table_list_lock); |
---|
1027 | 1051 | |
---|
1028 | 1052 | err = mr_ops->init(mlxsw_sp, mr->priv); |
---|
1029 | 1053 | if (err) |
---|
.. | .. |
---|
1035 | 1059 | mlxsw_core_schedule_dw(&mr->stats_update_dw, interval); |
---|
1036 | 1060 | return 0; |
---|
1037 | 1061 | err: |
---|
| 1062 | + mutex_destroy(&mr->table_list_lock); |
---|
1038 | 1063 | kfree(mr); |
---|
1039 | 1064 | return err; |
---|
1040 | 1065 | } |
---|
.. | .. |
---|
1045 | 1070 | |
---|
1046 | 1071 | cancel_delayed_work_sync(&mr->stats_update_dw); |
---|
1047 | 1072 | mr->mr_ops->fini(mlxsw_sp, mr->priv); |
---|
| 1073 | + mutex_destroy(&mr->table_list_lock); |
---|
1048 | 1074 | kfree(mr); |
---|
1049 | 1075 | } |
---|