1 /* 2 * Copyright (c) 2017, Mellanox Technologies. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 */ 32 33 #include <linux/etherdevice.h> 34 #include <linux/mlx5/driver.h> 35 #include <linux/mlx5/mlx5_ifc.h> 36 #include <linux/mlx5/mpfs.h> 37 #include <linux/mlx5/eswitch.h> 38 #include "mlx5_core.h" 39 #include "lib/mpfs.h" 40 41 /* HW L2 Table (MPFS) management */ 42 static int set_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac) 43 { 44 u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {}; 45 u8 *in_mac_addr; 46 47 MLX5_SET(set_l2_table_entry_in, in, opcode, MLX5_CMD_OP_SET_L2_TABLE_ENTRY); 48 MLX5_SET(set_l2_table_entry_in, in, table_index, index); 49 50 in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address); 51 ether_addr_copy(&in_mac_addr[2], mac); 52 53 return mlx5_cmd_exec_in(dev, set_l2_table_entry, in); 54 } 55 56 static int del_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index) 57 { 58 u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {}; 59 60 MLX5_SET(delete_l2_table_entry_in, in, opcode, MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY); 61 MLX5_SET(delete_l2_table_entry_in, in, table_index, index); 62 return mlx5_cmd_exec_in(dev, delete_l2_table_entry, in); 63 } 64 65 /* UC L2 table hash node */ 66 struct l2table_node { 67 struct l2addr_node node; 68 u32 index; /* index in HW l2 table */ 69 int ref_count; 70 }; 71 72 struct mlx5_mpfs { 73 struct hlist_head hash[MLX5_L2_ADDR_HASH_SIZE]; 74 struct mutex lock; /* Synchronize l2 table access */ 75 u32 size; 76 unsigned long *bitmap; 77 }; 78 79 static int alloc_l2table_index(struct mlx5_mpfs *l2table, u32 *ix) 80 { 81 int err = 0; 82 83 *ix = find_first_zero_bit(l2table->bitmap, l2table->size); 84 if (*ix >= l2table->size) 85 err = -ENOSPC; 86 else 87 __set_bit(*ix, l2table->bitmap); 88 89 return err; 90 } 91 92 static void free_l2table_index(struct mlx5_mpfs *l2table, u32 ix) 93 { 94 __clear_bit(ix, l2table->bitmap); 95 } 96 97 int mlx5_mpfs_init(struct mlx5_core_dev *dev) 98 { 99 int l2table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); 100 struct mlx5_mpfs *mpfs; 101 102 if (!MLX5_ESWITCH_MANAGER(dev)) 103 return 0; 104 105 mpfs = kzalloc(sizeof(*mpfs), GFP_KERNEL); 106 if (!mpfs) 107 return -ENOMEM; 108 109 mutex_init(&mpfs->lock); 110 mpfs->size = l2table_size; 111 mpfs->bitmap = bitmap_zalloc(l2table_size, GFP_KERNEL); 112 if (!mpfs->bitmap) { 113 kfree(mpfs); 114 return -ENOMEM; 115 } 116 117 dev->priv.mpfs = mpfs; 118 return 0; 119 } 120 121 void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev) 122 { 123 struct mlx5_mpfs *mpfs = dev->priv.mpfs; 124 125 if (!MLX5_ESWITCH_MANAGER(dev)) 126 return; 127 128 WARN_ON(!hlist_empty(mpfs->hash)); 129 bitmap_free(mpfs->bitmap); 130 kfree(mpfs); 131 } 132 133 int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac) 134 { 135 struct mlx5_mpfs *mpfs = dev->priv.mpfs; 136 struct l2table_node *l2addr; 137 int err = 0; 138 u32 index; 139 140 if (!MLX5_ESWITCH_MANAGER(dev)) 141 return 0; 142 143 mutex_lock(&mpfs->lock); 144 145 l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); 146 if (l2addr) { 147 l2addr->ref_count++; 148 goto out; 149 } 150 151 err = alloc_l2table_index(mpfs, &index); 152 if (err) 153 goto out; 154 155 l2addr = l2addr_hash_add(mpfs->hash, mac, struct l2table_node, GFP_KERNEL); 156 if (!l2addr) { 157 err = -ENOMEM; 158 goto hash_add_err; 159 } 160 161 err = set_l2table_entry_cmd(dev, index, mac); 162 if (err) 163 goto set_table_entry_err; 164 165 l2addr->index = index; 166 l2addr->ref_count = 1; 167 168 mlx5_core_dbg(dev, "MPFS mac added %pM, index (%d)\n", mac, index); 169 goto out; 170 171 set_table_entry_err: 172 l2addr_hash_del(l2addr); 173 hash_add_err: 174 free_l2table_index(mpfs, index); 175 out: 176 mutex_unlock(&mpfs->lock); 177 return err; 178 } 179 EXPORT_SYMBOL(mlx5_mpfs_add_mac); 180 181 int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac) 182 { 183 struct mlx5_mpfs *mpfs = dev->priv.mpfs; 184 struct l2table_node *l2addr; 185 int err = 0; 186 u32 index; 187 188 if (!MLX5_ESWITCH_MANAGER(dev)) 189 return 0; 190 191 mutex_lock(&mpfs->lock); 192 193 l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); 194 if (!l2addr) { 195 err = -ENOENT; 196 goto unlock; 197 } 198 199 if (--l2addr->ref_count > 0) 200 goto unlock; 201 202 index = l2addr->index; 203 del_l2table_entry_cmd(dev, index); 204 l2addr_hash_del(l2addr); 205 free_l2table_index(mpfs, index); 206 mlx5_core_dbg(dev, "MPFS mac deleted %pM, index (%d)\n", mac, index); 207 unlock: 208 mutex_unlock(&mpfs->lock); 209 return err; 210 } 211 EXPORT_SYMBOL(mlx5_mpfs_del_mac); 212