cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

legacy.c (13735B)


      1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
      2/* Copyright (c) 2021 Mellanox Technologies Ltd */
      3
      4#include <linux/etherdevice.h>
      5#include <linux/mlx5/driver.h>
      6#include <linux/mlx5/mlx5_ifc.h>
      7#include <linux/mlx5/vport.h>
      8#include <linux/mlx5/fs.h>
      9#include "esw/acl/lgcy.h"
     10#include "esw/legacy.h"
     11#include "mlx5_core.h"
     12#include "eswitch.h"
     13#include "fs_core.h"
     14#include "esw/qos.h"
     15
     16enum {
     17	LEGACY_VEPA_PRIO = 0,
     18	LEGACY_FDB_PRIO,
     19};
     20
     21static int esw_create_legacy_vepa_table(struct mlx5_eswitch *esw)
     22{
     23	struct mlx5_flow_table_attr ft_attr = {};
     24	struct mlx5_core_dev *dev = esw->dev;
     25	struct mlx5_flow_namespace *root_ns;
     26	struct mlx5_flow_table *fdb;
     27	int err;
     28
     29	root_ns = mlx5_get_fdb_sub_ns(dev, 0);
     30	if (!root_ns) {
     31		esw_warn(dev, "Failed to get FDB flow namespace\n");
     32		return -EOPNOTSUPP;
     33	}
     34
     35	/* num FTE 2, num FG 2 */
     36	ft_attr.prio = LEGACY_VEPA_PRIO;
     37	ft_attr.max_fte = 2;
     38	ft_attr.autogroup.max_num_groups = 2;
     39	fdb = mlx5_create_auto_grouped_flow_table(root_ns, &ft_attr);
     40	if (IS_ERR(fdb)) {
     41		err = PTR_ERR(fdb);
     42		esw_warn(dev, "Failed to create VEPA FDB err %d\n", err);
     43		return err;
     44	}
     45	esw->fdb_table.legacy.vepa_fdb = fdb;
     46
     47	return 0;
     48}
     49
     50static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw)
     51{
     52	esw_debug(esw->dev, "Destroy FDB Table\n");
     53	if (!esw->fdb_table.legacy.fdb)
     54		return;
     55
     56	if (esw->fdb_table.legacy.promisc_grp)
     57		mlx5_destroy_flow_group(esw->fdb_table.legacy.promisc_grp);
     58	if (esw->fdb_table.legacy.allmulti_grp)
     59		mlx5_destroy_flow_group(esw->fdb_table.legacy.allmulti_grp);
     60	if (esw->fdb_table.legacy.addr_grp)
     61		mlx5_destroy_flow_group(esw->fdb_table.legacy.addr_grp);
     62	mlx5_destroy_flow_table(esw->fdb_table.legacy.fdb);
     63
     64	esw->fdb_table.legacy.fdb = NULL;
     65	esw->fdb_table.legacy.addr_grp = NULL;
     66	esw->fdb_table.legacy.allmulti_grp = NULL;
     67	esw->fdb_table.legacy.promisc_grp = NULL;
     68	atomic64_set(&esw->user_count, 0);
     69}
     70
     71static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw)
     72{
     73	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
     74	struct mlx5_flow_table_attr ft_attr = {};
     75	struct mlx5_core_dev *dev = esw->dev;
     76	struct mlx5_flow_namespace *root_ns;
     77	struct mlx5_flow_table *fdb;
     78	struct mlx5_flow_group *g;
     79	void *match_criteria;
     80	int table_size;
     81	u32 *flow_group_in;
     82	u8 *dmac;
     83	int err = 0;
     84
     85	esw_debug(dev, "Create FDB log_max_size(%d)\n",
     86		  MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
     87
     88	root_ns = mlx5_get_fdb_sub_ns(dev, 0);
     89	if (!root_ns) {
     90		esw_warn(dev, "Failed to get FDB flow namespace\n");
     91		return -EOPNOTSUPP;
     92	}
     93
     94	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
     95	if (!flow_group_in)
     96		return -ENOMEM;
     97
     98	table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
     99	ft_attr.max_fte = table_size;
    100	ft_attr.prio = LEGACY_FDB_PRIO;
    101	fdb = mlx5_create_flow_table(root_ns, &ft_attr);
    102	if (IS_ERR(fdb)) {
    103		err = PTR_ERR(fdb);
    104		esw_warn(dev, "Failed to create FDB Table err %d\n", err);
    105		goto out;
    106	}
    107	esw->fdb_table.legacy.fdb = fdb;
    108
    109	/* Addresses group : Full match unicast/multicast addresses */
    110	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
    111		 MLX5_MATCH_OUTER_HEADERS);
    112	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
    113	dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16);
    114	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
    115	/* Preserve 2 entries for allmulti and promisc rules*/
    116	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 3);
    117	eth_broadcast_addr(dmac);
    118	g = mlx5_create_flow_group(fdb, flow_group_in);
    119	if (IS_ERR(g)) {
    120		err = PTR_ERR(g);
    121		esw_warn(dev, "Failed to create flow group err(%d)\n", err);
    122		goto out;
    123	}
    124	esw->fdb_table.legacy.addr_grp = g;
    125
    126	/* Allmulti group : One rule that forwards any mcast traffic */
    127	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
    128		 MLX5_MATCH_OUTER_HEADERS);
    129	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, table_size - 2);
    130	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 2);
    131	eth_zero_addr(dmac);
    132	dmac[0] = 0x01;
    133	g = mlx5_create_flow_group(fdb, flow_group_in);
    134	if (IS_ERR(g)) {
    135		err = PTR_ERR(g);
    136		esw_warn(dev, "Failed to create allmulti flow group err(%d)\n", err);
    137		goto out;
    138	}
    139	esw->fdb_table.legacy.allmulti_grp = g;
    140
    141	/* Promiscuous group :
    142	 * One rule that forward all unmatched traffic from previous groups
    143	 */
    144	eth_zero_addr(dmac);
    145	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
    146		 MLX5_MATCH_MISC_PARAMETERS);
    147	MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port);
    148	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, table_size - 1);
    149	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1);
    150	g = mlx5_create_flow_group(fdb, flow_group_in);
    151	if (IS_ERR(g)) {
    152		err = PTR_ERR(g);
    153		esw_warn(dev, "Failed to create promisc flow group err(%d)\n", err);
    154		goto out;
    155	}
    156	esw->fdb_table.legacy.promisc_grp = g;
    157
    158out:
    159	if (err)
    160		esw_destroy_legacy_fdb_table(esw);
    161
    162	kvfree(flow_group_in);
    163	return err;
    164}
    165
    166static void esw_destroy_legacy_vepa_table(struct mlx5_eswitch *esw)
    167{
    168	esw_debug(esw->dev, "Destroy VEPA Table\n");
    169	if (!esw->fdb_table.legacy.vepa_fdb)
    170		return;
    171
    172	mlx5_destroy_flow_table(esw->fdb_table.legacy.vepa_fdb);
    173	esw->fdb_table.legacy.vepa_fdb = NULL;
    174}
    175
    176static int esw_create_legacy_table(struct mlx5_eswitch *esw)
    177{
    178	int err;
    179
    180	memset(&esw->fdb_table.legacy, 0, sizeof(struct legacy_fdb));
    181	atomic64_set(&esw->user_count, 0);
    182
    183	err = esw_create_legacy_vepa_table(esw);
    184	if (err)
    185		return err;
    186
    187	err = esw_create_legacy_fdb_table(esw);
    188	if (err)
    189		esw_destroy_legacy_vepa_table(esw);
    190
    191	return err;
    192}
    193
    194static void esw_cleanup_vepa_rules(struct mlx5_eswitch *esw)
    195{
    196	if (esw->fdb_table.legacy.vepa_uplink_rule)
    197		mlx5_del_flow_rules(esw->fdb_table.legacy.vepa_uplink_rule);
    198
    199	if (esw->fdb_table.legacy.vepa_star_rule)
    200		mlx5_del_flow_rules(esw->fdb_table.legacy.vepa_star_rule);
    201
    202	esw->fdb_table.legacy.vepa_uplink_rule = NULL;
    203	esw->fdb_table.legacy.vepa_star_rule = NULL;
    204}
    205
    206static void esw_destroy_legacy_table(struct mlx5_eswitch *esw)
    207{
    208	esw_cleanup_vepa_rules(esw);
    209	esw_destroy_legacy_fdb_table(esw);
    210	esw_destroy_legacy_vepa_table(esw);
    211}
    212
    213#define MLX5_LEGACY_SRIOV_VPORT_EVENTS (MLX5_VPORT_UC_ADDR_CHANGE | \
    214					MLX5_VPORT_MC_ADDR_CHANGE | \
    215					MLX5_VPORT_PROMISC_CHANGE)
    216
    217int esw_legacy_enable(struct mlx5_eswitch *esw)
    218{
    219	struct mlx5_vport *vport;
    220	unsigned long i;
    221	int ret;
    222
    223	ret = esw_create_legacy_table(esw);
    224	if (ret)
    225		return ret;
    226
    227	mlx5_esw_for_each_vf_vport(esw, i, vport, esw->esw_funcs.num_vfs)
    228		vport->info.link_state = MLX5_VPORT_ADMIN_STATE_AUTO;
    229
    230	ret = mlx5_eswitch_enable_pf_vf_vports(esw, MLX5_LEGACY_SRIOV_VPORT_EVENTS);
    231	if (ret)
    232		esw_destroy_legacy_table(esw);
    233	return ret;
    234}
    235
    236void esw_legacy_disable(struct mlx5_eswitch *esw)
    237{
    238	struct esw_mc_addr *mc_promisc;
    239
    240	mlx5_eswitch_disable_pf_vf_vports(esw);
    241
    242	mc_promisc = &esw->mc_promisc;
    243	if (mc_promisc->uplink_rule)
    244		mlx5_del_flow_rules(mc_promisc->uplink_rule);
    245
    246	esw_destroy_legacy_table(esw);
    247}
    248
    249static int _mlx5_eswitch_set_vepa_locked(struct mlx5_eswitch *esw,
    250					 u8 setting)
    251{
    252	struct mlx5_flow_destination dest = {};
    253	struct mlx5_flow_act flow_act = {};
    254	struct mlx5_flow_handle *flow_rule;
    255	struct mlx5_flow_spec *spec;
    256	int err = 0;
    257	void *misc;
    258
    259	if (!setting) {
    260		esw_cleanup_vepa_rules(esw);
    261		return 0;
    262	}
    263
    264	if (esw->fdb_table.legacy.vepa_uplink_rule)
    265		return 0;
    266
    267	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
    268	if (!spec)
    269		return -ENOMEM;
    270
    271	/* Uplink rule forward uplink traffic to FDB */
    272	misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
    273	MLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_UPLINK);
    274
    275	misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
    276	MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
    277
    278	spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
    279	dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
    280	dest.ft = esw->fdb_table.legacy.fdb;
    281	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
    282	flow_rule = mlx5_add_flow_rules(esw->fdb_table.legacy.vepa_fdb, spec,
    283					&flow_act, &dest, 1);
    284	if (IS_ERR(flow_rule)) {
    285		err = PTR_ERR(flow_rule);
    286		goto out;
    287	} else {
    288		esw->fdb_table.legacy.vepa_uplink_rule = flow_rule;
    289	}
    290
    291	/* Star rule to forward all traffic to uplink vport */
    292	memset(&dest, 0, sizeof(dest));
    293	dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
    294	dest.vport.num = MLX5_VPORT_UPLINK;
    295	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
    296	flow_rule = mlx5_add_flow_rules(esw->fdb_table.legacy.vepa_fdb, NULL,
    297					&flow_act, &dest, 1);
    298	if (IS_ERR(flow_rule)) {
    299		err = PTR_ERR(flow_rule);
    300		goto out;
    301	} else {
    302		esw->fdb_table.legacy.vepa_star_rule = flow_rule;
    303	}
    304
    305out:
    306	kvfree(spec);
    307	if (err)
    308		esw_cleanup_vepa_rules(esw);
    309	return err;
    310}
    311
    312int mlx5_eswitch_set_vepa(struct mlx5_eswitch *esw, u8 setting)
    313{
    314	int err = 0;
    315
    316	if (!esw)
    317		return -EOPNOTSUPP;
    318
    319	if (!mlx5_esw_allowed(esw))
    320		return -EPERM;
    321
    322	mutex_lock(&esw->state_lock);
    323	if (esw->mode != MLX5_ESWITCH_LEGACY) {
    324		err = -EOPNOTSUPP;
    325		goto out;
    326	}
    327
    328	err = _mlx5_eswitch_set_vepa_locked(esw, setting);
    329
    330out:
    331	mutex_unlock(&esw->state_lock);
    332	return err;
    333}
    334
    335int mlx5_eswitch_get_vepa(struct mlx5_eswitch *esw, u8 *setting)
    336{
    337	if (!esw)
    338		return -EOPNOTSUPP;
    339
    340	if (!mlx5_esw_allowed(esw))
    341		return -EPERM;
    342
    343	if (esw->mode != MLX5_ESWITCH_LEGACY)
    344		return -EOPNOTSUPP;
    345
    346	*setting = esw->fdb_table.legacy.vepa_uplink_rule ? 1 : 0;
    347	return 0;
    348}
    349
    350int esw_legacy_vport_acl_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
    351{
    352	int ret;
    353
    354	/* Only non manager vports need ACL in legacy mode */
    355	if (mlx5_esw_is_manager_vport(esw, vport->vport))
    356		return 0;
    357
    358	ret = esw_acl_ingress_lgcy_setup(esw, vport);
    359	if (ret)
    360		goto ingress_err;
    361
    362	ret = esw_acl_egress_lgcy_setup(esw, vport);
    363	if (ret)
    364		goto egress_err;
    365
    366	return 0;
    367
    368egress_err:
    369	esw_acl_ingress_lgcy_cleanup(esw, vport);
    370ingress_err:
    371	return ret;
    372}
    373
    374void esw_legacy_vport_acl_cleanup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
    375{
    376	if (mlx5_esw_is_manager_vport(esw, vport->vport))
    377		return;
    378
    379	esw_acl_egress_lgcy_cleanup(esw, vport);
    380	esw_acl_ingress_lgcy_cleanup(esw, vport);
    381}
    382
    383int mlx5_esw_query_vport_drop_stats(struct mlx5_core_dev *dev,
    384				    struct mlx5_vport *vport,
    385				    struct mlx5_vport_drop_stats *stats)
    386{
    387	u64 rx_discard_vport_down, tx_discard_vport_down;
    388	struct mlx5_eswitch *esw = dev->priv.eswitch;
    389	u64 bytes = 0;
    390	int err = 0;
    391
    392	if (esw->mode != MLX5_ESWITCH_LEGACY)
    393		return 0;
    394
    395	mutex_lock(&esw->state_lock);
    396	if (!vport->enabled)
    397		goto unlock;
    398
    399	if (!IS_ERR_OR_NULL(vport->egress.legacy.drop_counter))
    400		mlx5_fc_query(dev, vport->egress.legacy.drop_counter,
    401			      &stats->rx_dropped, &bytes);
    402
    403	if (vport->ingress.legacy.drop_counter)
    404		mlx5_fc_query(dev, vport->ingress.legacy.drop_counter,
    405			      &stats->tx_dropped, &bytes);
    406
    407	if (!MLX5_CAP_GEN(dev, receive_discard_vport_down) &&
    408	    !MLX5_CAP_GEN(dev, transmit_discard_vport_down))
    409		goto unlock;
    410
    411	err = mlx5_query_vport_down_stats(dev, vport->vport, 1,
    412					  &rx_discard_vport_down,
    413					  &tx_discard_vport_down);
    414	if (err)
    415		goto unlock;
    416
    417	if (MLX5_CAP_GEN(dev, receive_discard_vport_down))
    418		stats->rx_dropped += rx_discard_vport_down;
    419	if (MLX5_CAP_GEN(dev, transmit_discard_vport_down))
    420		stats->tx_dropped += tx_discard_vport_down;
    421
    422unlock:
    423	mutex_unlock(&esw->state_lock);
    424	return err;
    425}
    426
    427int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
    428				u16 vport, u16 vlan, u8 qos)
    429{
    430	u8 set_flags = 0;
    431	int err = 0;
    432
    433	if (!mlx5_esw_allowed(esw))
    434		return vlan ? -EPERM : 0;
    435
    436	if (vlan || qos)
    437		set_flags = SET_VLAN_STRIP | SET_VLAN_INSERT;
    438
    439	mutex_lock(&esw->state_lock);
    440	if (esw->mode != MLX5_ESWITCH_LEGACY) {
    441		if (!vlan)
    442			goto unlock; /* compatibility with libvirt */
    443
    444		err = -EOPNOTSUPP;
    445		goto unlock;
    446	}
    447
    448	err = __mlx5_eswitch_set_vport_vlan(esw, vport, vlan, qos, set_flags);
    449
    450unlock:
    451	mutex_unlock(&esw->state_lock);
    452	return err;
    453}
    454
    455int mlx5_eswitch_set_vport_spoofchk(struct mlx5_eswitch *esw,
    456				    u16 vport, bool spoofchk)
    457{
    458	struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport);
    459	bool pschk;
    460	int err = 0;
    461
    462	if (!mlx5_esw_allowed(esw))
    463		return -EPERM;
    464	if (IS_ERR(evport))
    465		return PTR_ERR(evport);
    466
    467	mutex_lock(&esw->state_lock);
    468	if (esw->mode != MLX5_ESWITCH_LEGACY) {
    469		err = -EOPNOTSUPP;
    470		goto unlock;
    471	}
    472	pschk = evport->info.spoofchk;
    473	evport->info.spoofchk = spoofchk;
    474	if (pschk && !is_valid_ether_addr(evport->info.mac))
    475		mlx5_core_warn(esw->dev,
    476			       "Spoofchk in set while MAC is invalid, vport(%d)\n",
    477			       evport->vport);
    478	if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY)
    479		err = esw_acl_ingress_lgcy_setup(esw, evport);
    480	if (err)
    481		evport->info.spoofchk = pschk;
    482
    483unlock:
    484	mutex_unlock(&esw->state_lock);
    485	return err;
    486}
    487
    488int mlx5_eswitch_set_vport_trust(struct mlx5_eswitch *esw,
    489				 u16 vport, bool setting)
    490{
    491	struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport);
    492	int err = 0;
    493
    494	if (!mlx5_esw_allowed(esw))
    495		return -EPERM;
    496	if (IS_ERR(evport))
    497		return PTR_ERR(evport);
    498
    499	mutex_lock(&esw->state_lock);
    500	if (esw->mode != MLX5_ESWITCH_LEGACY) {
    501		err = -EOPNOTSUPP;
    502		goto unlock;
    503	}
    504	evport->info.trusted = setting;
    505	if (evport->enabled)
    506		esw_vport_change_handle_locked(evport);
    507
    508unlock:
    509	mutex_unlock(&esw->state_lock);
    510	return err;
    511}
    512
    513int mlx5_eswitch_set_vport_rate(struct mlx5_eswitch *esw, u16 vport,
    514				u32 max_rate, u32 min_rate)
    515{
    516	struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport);
    517	int err;
    518
    519	if (!mlx5_esw_allowed(esw))
    520		return -EPERM;
    521	if (IS_ERR(evport))
    522		return PTR_ERR(evport);
    523
    524	mutex_lock(&esw->state_lock);
    525	err = mlx5_esw_qos_set_vport_rate(esw, evport, max_rate, min_rate);
    526	mutex_unlock(&esw->state_lock);
    527	return err;
    528}