| .. | .. |
|---|
| 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 | } |
|---|