mpfs.c (5484B)
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 */ 42static 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 56static 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 */ 66struct l2table_node { 67 struct l2addr_node node; 68 u32 index; /* index in HW l2 table */ 69 int ref_count; 70}; 71 72struct 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 79static 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 92static void free_l2table_index(struct mlx5_mpfs *l2table, u32 ix) 93{ 94 __clear_bit(ix, l2table->bitmap); 95} 96 97int 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 121void 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 133int 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 171set_table_entry_err: 172 l2addr_hash_del(l2addr); 173hash_add_err: 174 free_l2table_index(mpfs, index); 175out: 176 mutex_unlock(&mpfs->lock); 177 return err; 178} 179EXPORT_SYMBOL(mlx5_mpfs_add_mac); 180 181int 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); 207unlock: 208 mutex_unlock(&mpfs->lock); 209 return err; 210} 211EXPORT_SYMBOL(mlx5_mpfs_del_mac);