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/eswitch.h> 37 #include "mlx5_core.h" 38 #include "lib/mpfs.h" 39 40 /* HW L2 Table (MPFS) management */ 41 static int set_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac) 42 { 43 u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {}; 44 u8 *in_mac_addr; 45 46 MLX5_SET(set_l2_table_entry_in, in, opcode, MLX5_CMD_OP_SET_L2_TABLE_ENTRY); 47 MLX5_SET(set_l2_table_entry_in, in, table_index, index); 48 49 in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address); 50 ether_addr_copy(&in_mac_addr[2], mac); 51 52 return mlx5_cmd_exec_in(dev, set_l2_table_entry, in); 53 } 54 55 static int del_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index) 56 { 57 u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {}; 58 59 MLX5_SET(delete_l2_table_entry_in, in, opcode, MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY); 60 MLX5_SET(delete_l2_table_entry_in, in, table_index, index); 61 return mlx5_cmd_exec_in(dev, delete_l2_table_entry, in); 62 } 63 64 /* UC L2 table hash node */ 65 struct l2table_node { 66 struct l2addr_node node; 67 u32 index; /* index in HW l2 table */ 68 int ref_count; 69 }; 70 71 struct mlx5_mpfs { 72 struct hlist_head hash[MLX5_L2_ADDR_HASH_SIZE]; 73 struct mutex lock; /* Synchronize l2 table access */ 74 u32 size; 75 unsigned long *bitmap; 76 }; 77 78 static int alloc_l2table_index(struct mlx5_mpfs *l2table, u32 *ix) 79 { 80 int err = 0; 81 82 *ix = find_first_zero_bit(l2table->bitmap, l2table->size); 83 if (*ix >= l2table->size) 84 err = -ENOSPC; 85 else 86 __set_bit(*ix, l2table->bitmap); 87 88 return err; 89 } 90 91 static void free_l2table_index(struct mlx5_mpfs *l2table, u32 ix) 92 { 93 __clear_bit(ix, l2table->bitmap); 94 } 95 96 int mlx5_mpfs_init(struct mlx5_core_dev *dev) 97 { 98 int l2table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); 99 struct mlx5_mpfs *mpfs; 100 101 if (!MLX5_ESWITCH_MANAGER(dev)) 102 return 0; 103 104 mpfs = kzalloc(sizeof(*mpfs), GFP_KERNEL); 105 if (!mpfs) 106 return -ENOMEM; 107 108 mutex_init(&mpfs->lock); 109 mpfs->size = l2table_size; 110 mpfs->bitmap = bitmap_zalloc(l2table_size, GFP_KERNEL); 111 if (!mpfs->bitmap) { 112 kfree(mpfs); 113 return -ENOMEM; 114 } 115 116 dev->priv.mpfs = mpfs; 117 return 0; 118 } 119 120 void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev) 121 { 122 struct mlx5_mpfs *mpfs = dev->priv.mpfs; 123 124 if (!MLX5_ESWITCH_MANAGER(dev)) 125 return; 126 127 WARN_ON(!hlist_empty(mpfs->hash)); 128 bitmap_free(mpfs->bitmap); 129 kfree(mpfs); 130 } 131 132 int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac) 133 { 134 struct mlx5_mpfs *mpfs = dev->priv.mpfs; 135 struct l2table_node *l2addr; 136 int err = 0; 137 u32 index; 138 139 if (!MLX5_ESWITCH_MANAGER(dev)) 140 return 0; 141 142 mutex_lock(&mpfs->lock); 143 144 l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); 145 if (l2addr) { 146 l2addr->ref_count++; 147 goto out; 148 } 149 150 err = alloc_l2table_index(mpfs, &index); 151 if (err) 152 goto out; 153 154 l2addr = l2addr_hash_add(mpfs->hash, mac, struct l2table_node, GFP_KERNEL); 155 if (!l2addr) { 156 err = -ENOMEM; 157 goto hash_add_err; 158 } 159 160 err = set_l2table_entry_cmd(dev, index, mac); 161 if (err) 162 goto set_table_entry_err; 163 164 l2addr->index = index; 165 l2addr->ref_count = 1; 166 167 mlx5_core_dbg(dev, "MPFS mac added %pM, index (%d)\n", mac, index); 168 goto out; 169 170 set_table_entry_err: 171 l2addr_hash_del(l2addr); 172 hash_add_err: 173 free_l2table_index(mpfs, index); 174 out: 175 mutex_unlock(&mpfs->lock); 176 return err; 177 } 178 179 int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac) 180 { 181 struct mlx5_mpfs *mpfs = dev->priv.mpfs; 182 struct l2table_node *l2addr; 183 int err = 0; 184 u32 index; 185 186 if (!MLX5_ESWITCH_MANAGER(dev)) 187 return 0; 188 189 mutex_lock(&mpfs->lock); 190 191 l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); 192 if (!l2addr) { 193 err = -ENOENT; 194 goto unlock; 195 } 196 197 if (--l2addr->ref_count > 0) 198 goto unlock; 199 200 index = l2addr->index; 201 del_l2table_entry_cmd(dev, index); 202 l2addr_hash_del(l2addr); 203 free_l2table_index(mpfs, index); 204 mlx5_core_dbg(dev, "MPFS mac deleted %pM, index (%d)\n", mac, index); 205 unlock: 206 mutex_unlock(&mpfs->lock); 207 return err; 208 } 209