0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 #include <linux/etherdevice.h>
0034 #include <linux/mlx5/driver.h>
0035 #include <linux/mlx5/mlx5_ifc.h>
0036 #include <linux/mlx5/mpfs.h>
0037 #include <linux/mlx5/eswitch.h>
0038 #include "mlx5_core.h"
0039 #include "lib/mpfs.h"
0040
0041
0042 static int set_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac)
0043 {
0044 u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {};
0045 u8 *in_mac_addr;
0046
0047 MLX5_SET(set_l2_table_entry_in, in, opcode, MLX5_CMD_OP_SET_L2_TABLE_ENTRY);
0048 MLX5_SET(set_l2_table_entry_in, in, table_index, index);
0049
0050 in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address);
0051 ether_addr_copy(&in_mac_addr[2], mac);
0052
0053 return mlx5_cmd_exec_in(dev, set_l2_table_entry, in);
0054 }
0055
0056 static int del_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index)
0057 {
0058 u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {};
0059
0060 MLX5_SET(delete_l2_table_entry_in, in, opcode, MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY);
0061 MLX5_SET(delete_l2_table_entry_in, in, table_index, index);
0062 return mlx5_cmd_exec_in(dev, delete_l2_table_entry, in);
0063 }
0064
0065
0066 struct l2table_node {
0067 struct l2addr_node node;
0068 u32 index;
0069 int ref_count;
0070 };
0071
0072 struct mlx5_mpfs {
0073 struct hlist_head hash[MLX5_L2_ADDR_HASH_SIZE];
0074 struct mutex lock;
0075 u32 size;
0076 unsigned long *bitmap;
0077 };
0078
0079 static int alloc_l2table_index(struct mlx5_mpfs *l2table, u32 *ix)
0080 {
0081 int err = 0;
0082
0083 *ix = find_first_zero_bit(l2table->bitmap, l2table->size);
0084 if (*ix >= l2table->size)
0085 err = -ENOSPC;
0086 else
0087 __set_bit(*ix, l2table->bitmap);
0088
0089 return err;
0090 }
0091
0092 static void free_l2table_index(struct mlx5_mpfs *l2table, u32 ix)
0093 {
0094 __clear_bit(ix, l2table->bitmap);
0095 }
0096
0097 int mlx5_mpfs_init(struct mlx5_core_dev *dev)
0098 {
0099 int l2table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table);
0100 struct mlx5_mpfs *mpfs;
0101
0102 if (!MLX5_ESWITCH_MANAGER(dev))
0103 return 0;
0104
0105 mpfs = kzalloc(sizeof(*mpfs), GFP_KERNEL);
0106 if (!mpfs)
0107 return -ENOMEM;
0108
0109 mutex_init(&mpfs->lock);
0110 mpfs->size = l2table_size;
0111 mpfs->bitmap = bitmap_zalloc(l2table_size, GFP_KERNEL);
0112 if (!mpfs->bitmap) {
0113 kfree(mpfs);
0114 return -ENOMEM;
0115 }
0116
0117 dev->priv.mpfs = mpfs;
0118 return 0;
0119 }
0120
0121 void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev)
0122 {
0123 struct mlx5_mpfs *mpfs = dev->priv.mpfs;
0124
0125 if (!MLX5_ESWITCH_MANAGER(dev))
0126 return;
0127
0128 WARN_ON(!hlist_empty(mpfs->hash));
0129 bitmap_free(mpfs->bitmap);
0130 kfree(mpfs);
0131 }
0132
0133 int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac)
0134 {
0135 struct mlx5_mpfs *mpfs = dev->priv.mpfs;
0136 struct l2table_node *l2addr;
0137 int err = 0;
0138 u32 index;
0139
0140 if (!MLX5_ESWITCH_MANAGER(dev))
0141 return 0;
0142
0143 mutex_lock(&mpfs->lock);
0144
0145 l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node);
0146 if (l2addr) {
0147 l2addr->ref_count++;
0148 goto out;
0149 }
0150
0151 err = alloc_l2table_index(mpfs, &index);
0152 if (err)
0153 goto out;
0154
0155 l2addr = l2addr_hash_add(mpfs->hash, mac, struct l2table_node, GFP_KERNEL);
0156 if (!l2addr) {
0157 err = -ENOMEM;
0158 goto hash_add_err;
0159 }
0160
0161 err = set_l2table_entry_cmd(dev, index, mac);
0162 if (err)
0163 goto set_table_entry_err;
0164
0165 l2addr->index = index;
0166 l2addr->ref_count = 1;
0167
0168 mlx5_core_dbg(dev, "MPFS mac added %pM, index (%d)\n", mac, index);
0169 goto out;
0170
0171 set_table_entry_err:
0172 l2addr_hash_del(l2addr);
0173 hash_add_err:
0174 free_l2table_index(mpfs, index);
0175 out:
0176 mutex_unlock(&mpfs->lock);
0177 return err;
0178 }
0179 EXPORT_SYMBOL(mlx5_mpfs_add_mac);
0180
0181 int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac)
0182 {
0183 struct mlx5_mpfs *mpfs = dev->priv.mpfs;
0184 struct l2table_node *l2addr;
0185 int err = 0;
0186 u32 index;
0187
0188 if (!MLX5_ESWITCH_MANAGER(dev))
0189 return 0;
0190
0191 mutex_lock(&mpfs->lock);
0192
0193 l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node);
0194 if (!l2addr) {
0195 err = -ENOENT;
0196 goto unlock;
0197 }
0198
0199 if (--l2addr->ref_count > 0)
0200 goto unlock;
0201
0202 index = l2addr->index;
0203 del_l2table_entry_cmd(dev, index);
0204 l2addr_hash_del(l2addr);
0205 free_l2table_index(mpfs, index);
0206 mlx5_core_dbg(dev, "MPFS mac deleted %pM, index (%d)\n", mac, index);
0207 unlock:
0208 mutex_unlock(&mpfs->lock);
0209 return err;
0210 }
0211 EXPORT_SYMBOL(mlx5_mpfs_del_mac);