| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */ |
| |
| #include "macsec.h" |
| #include <linux/mlx5/macsec.h> |
| |
| struct mlx5_reserved_gids { |
| int macsec_index; |
| const struct ib_gid_attr *physical_gid; |
| }; |
| |
| struct mlx5_roce_gids { |
| struct list_head roce_gid_list_entry; |
| u16 gid_idx; |
| union { |
| struct sockaddr_in sockaddr_in; |
| struct sockaddr_in6 sockaddr_in6; |
| } addr; |
| }; |
| |
| struct mlx5_macsec_device { |
| struct list_head macsec_devices_list_entry; |
| void *macdev; |
| struct list_head macsec_roce_gids; |
| struct list_head tx_rules_list; |
| struct list_head rx_rules_list; |
| }; |
| |
| static void cleanup_macsec_device(struct mlx5_macsec_device *macsec_device) |
| { |
| if (!list_empty(&macsec_device->tx_rules_list) || |
| !list_empty(&macsec_device->rx_rules_list) || |
| !list_empty(&macsec_device->macsec_roce_gids)) |
| return; |
| |
| list_del(&macsec_device->macsec_devices_list_entry); |
| kfree(macsec_device); |
| } |
| |
| static struct mlx5_macsec_device *get_macsec_device(void *macdev, |
| struct list_head *macsec_devices_list) |
| { |
| struct mlx5_macsec_device *iter, *macsec_device = NULL; |
| |
| list_for_each_entry(iter, macsec_devices_list, macsec_devices_list_entry) { |
| if (iter->macdev == macdev) { |
| macsec_device = iter; |
| break; |
| } |
| } |
| |
| if (macsec_device) |
| return macsec_device; |
| |
| macsec_device = kzalloc(sizeof(*macsec_device), GFP_KERNEL); |
| if (!macsec_device) |
| return NULL; |
| |
| macsec_device->macdev = macdev; |
| INIT_LIST_HEAD(&macsec_device->tx_rules_list); |
| INIT_LIST_HEAD(&macsec_device->rx_rules_list); |
| INIT_LIST_HEAD(&macsec_device->macsec_roce_gids); |
| list_add(&macsec_device->macsec_devices_list_entry, macsec_devices_list); |
| |
| return macsec_device; |
| } |
| |
| static void mlx5_macsec_del_roce_gid(struct mlx5_macsec_device *macsec_device, u16 gid_idx) |
| { |
| struct mlx5_roce_gids *current_gid, *next_gid; |
| |
| list_for_each_entry_safe(current_gid, next_gid, &macsec_device->macsec_roce_gids, |
| roce_gid_list_entry) |
| if (current_gid->gid_idx == gid_idx) { |
| list_del(¤t_gid->roce_gid_list_entry); |
| kfree(current_gid); |
| } |
| } |
| |
| static void mlx5_macsec_save_roce_gid(struct mlx5_macsec_device *macsec_device, |
| const struct sockaddr *addr, u16 gid_idx) |
| { |
| struct mlx5_roce_gids *roce_gids; |
| |
| roce_gids = kzalloc(sizeof(*roce_gids), GFP_KERNEL); |
| if (!roce_gids) |
| return; |
| |
| roce_gids->gid_idx = gid_idx; |
| if (addr->sa_family == AF_INET) |
| memcpy(&roce_gids->addr.sockaddr_in, addr, sizeof(roce_gids->addr.sockaddr_in)); |
| else |
| memcpy(&roce_gids->addr.sockaddr_in6, addr, sizeof(roce_gids->addr.sockaddr_in6)); |
| |
| list_add_tail(&roce_gids->roce_gid_list_entry, &macsec_device->macsec_roce_gids); |
| } |
| |
| static void handle_macsec_gids(struct list_head *macsec_devices_list, |
| struct mlx5_macsec_event_data *data) |
| { |
| struct mlx5_macsec_device *macsec_device; |
| struct mlx5_roce_gids *gid; |
| |
| macsec_device = get_macsec_device(data->macdev, macsec_devices_list); |
| if (!macsec_device) |
| return; |
| |
| list_for_each_entry(gid, &macsec_device->macsec_roce_gids, roce_gid_list_entry) { |
| mlx5_macsec_add_roce_sa_rules(data->fs_id, (struct sockaddr *)&gid->addr, |
| gid->gid_idx, &macsec_device->tx_rules_list, |
| &macsec_device->rx_rules_list, data->macsec_fs, |
| data->is_tx); |
| } |
| } |
| |
| static void del_sa_roce_rule(struct list_head *macsec_devices_list, |
| struct mlx5_macsec_event_data *data) |
| { |
| struct mlx5_macsec_device *macsec_device; |
| |
| macsec_device = get_macsec_device(data->macdev, macsec_devices_list); |
| WARN_ON(!macsec_device); |
| |
| mlx5_macsec_del_roce_sa_rules(data->fs_id, data->macsec_fs, |
| &macsec_device->tx_rules_list, |
| &macsec_device->rx_rules_list, data->is_tx); |
| } |
| |
| static int macsec_event(struct notifier_block *nb, unsigned long event, void *data) |
| { |
| struct mlx5_macsec *macsec = container_of(nb, struct mlx5_macsec, blocking_events_nb); |
| |
| mutex_lock(&macsec->lock); |
| switch (event) { |
| case MLX5_DRIVER_EVENT_MACSEC_SA_ADDED: |
| handle_macsec_gids(&macsec->macsec_devices_list, data); |
| break; |
| case MLX5_DRIVER_EVENT_MACSEC_SA_DELETED: |
| del_sa_roce_rule(&macsec->macsec_devices_list, data); |
| break; |
| default: |
| mutex_unlock(&macsec->lock); |
| return NOTIFY_DONE; |
| } |
| mutex_unlock(&macsec->lock); |
| return NOTIFY_OK; |
| } |
| |
| void mlx5r_macsec_event_register(struct mlx5_ib_dev *dev) |
| { |
| if (!mlx5_is_macsec_roce_supported(dev->mdev)) { |
| mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n"); |
| return; |
| } |
| |
| dev->macsec.blocking_events_nb.notifier_call = macsec_event; |
| blocking_notifier_chain_register(&dev->mdev->macsec_nh, |
| &dev->macsec.blocking_events_nb); |
| } |
| |
| void mlx5r_macsec_event_unregister(struct mlx5_ib_dev *dev) |
| { |
| if (!mlx5_is_macsec_roce_supported(dev->mdev)) { |
| mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n"); |
| return; |
| } |
| |
| blocking_notifier_chain_unregister(&dev->mdev->macsec_nh, |
| &dev->macsec.blocking_events_nb); |
| } |
| |
| int mlx5r_macsec_init_gids_and_devlist(struct mlx5_ib_dev *dev) |
| { |
| int i, j, max_gids; |
| |
| if (!mlx5_is_macsec_roce_supported(dev->mdev)) { |
| mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n"); |
| return 0; |
| } |
| |
| max_gids = MLX5_CAP_ROCE(dev->mdev, roce_address_table_size); |
| for (i = 0; i < dev->num_ports; i++) { |
| dev->port[i].reserved_gids = kcalloc(max_gids, |
| sizeof(*dev->port[i].reserved_gids), |
| GFP_KERNEL); |
| if (!dev->port[i].reserved_gids) |
| goto err; |
| |
| for (j = 0; j < max_gids; j++) |
| dev->port[i].reserved_gids[j].macsec_index = -1; |
| } |
| |
| INIT_LIST_HEAD(&dev->macsec.macsec_devices_list); |
| mutex_init(&dev->macsec.lock); |
| |
| return 0; |
| err: |
| while (i >= 0) { |
| kfree(dev->port[i].reserved_gids); |
| i--; |
| } |
| return -ENOMEM; |
| } |
| |
| void mlx5r_macsec_dealloc_gids(struct mlx5_ib_dev *dev) |
| { |
| int i; |
| |
| if (!mlx5_is_macsec_roce_supported(dev->mdev)) |
| mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n"); |
| |
| for (i = 0; i < dev->num_ports; i++) |
| kfree(dev->port[i].reserved_gids); |
| |
| mutex_destroy(&dev->macsec.lock); |
| } |
| |
| int mlx5r_add_gid_macsec_operations(const struct ib_gid_attr *attr) |
| { |
| struct mlx5_ib_dev *dev = to_mdev(attr->device); |
| struct mlx5_macsec_device *macsec_device; |
| const struct ib_gid_attr *physical_gid; |
| struct mlx5_reserved_gids *mgids; |
| struct net_device *ndev; |
| int ret = 0; |
| union { |
| struct sockaddr_in sockaddr_in; |
| struct sockaddr_in6 sockaddr_in6; |
| } addr; |
| |
| if (attr->gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP) |
| return 0; |
| |
| if (!mlx5_is_macsec_roce_supported(dev->mdev)) { |
| mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n"); |
| return 0; |
| } |
| |
| rcu_read_lock(); |
| ndev = rcu_dereference(attr->ndev); |
| if (!ndev) { |
| rcu_read_unlock(); |
| return -ENODEV; |
| } |
| |
| if (!netif_is_macsec(ndev) || !macsec_netdev_is_offloaded(ndev)) { |
| rcu_read_unlock(); |
| return 0; |
| } |
| dev_hold(ndev); |
| rcu_read_unlock(); |
| |
| mutex_lock(&dev->macsec.lock); |
| macsec_device = get_macsec_device(ndev, &dev->macsec.macsec_devices_list); |
| if (!macsec_device) { |
| ret = -ENOMEM; |
| goto dev_err; |
| } |
| |
| physical_gid = rdma_find_gid(attr->device, &attr->gid, |
| attr->gid_type, NULL); |
| if (!IS_ERR(physical_gid)) { |
| ret = set_roce_addr(to_mdev(physical_gid->device), |
| physical_gid->port_num, |
| physical_gid->index, NULL, |
| physical_gid); |
| if (ret) |
| goto gid_err; |
| |
| mgids = &dev->port[attr->port_num - 1].reserved_gids[physical_gid->index]; |
| mgids->macsec_index = attr->index; |
| mgids->physical_gid = physical_gid; |
| } |
| |
| /* Proceed with adding steering rules, regardless if there was gid ambiguity or not.*/ |
| rdma_gid2ip((struct sockaddr *)&addr, &attr->gid); |
| ret = mlx5_macsec_add_roce_rule(ndev, (struct sockaddr *)&addr, attr->index, |
| &macsec_device->tx_rules_list, |
| &macsec_device->rx_rules_list, dev->mdev->macsec_fs); |
| if (ret && !IS_ERR(physical_gid)) |
| goto rule_err; |
| |
| mlx5_macsec_save_roce_gid(macsec_device, (struct sockaddr *)&addr, attr->index); |
| |
| dev_put(ndev); |
| mutex_unlock(&dev->macsec.lock); |
| return ret; |
| |
| rule_err: |
| set_roce_addr(to_mdev(physical_gid->device), physical_gid->port_num, |
| physical_gid->index, &physical_gid->gid, physical_gid); |
| mgids->macsec_index = -1; |
| gid_err: |
| rdma_put_gid_attr(physical_gid); |
| cleanup_macsec_device(macsec_device); |
| dev_err: |
| dev_put(ndev); |
| mutex_unlock(&dev->macsec.lock); |
| return ret; |
| } |
| |
| void mlx5r_del_gid_macsec_operations(const struct ib_gid_attr *attr) |
| { |
| struct mlx5_ib_dev *dev = to_mdev(attr->device); |
| struct mlx5_macsec_device *macsec_device; |
| struct mlx5_reserved_gids *mgids; |
| struct net_device *ndev; |
| int i, max_gids; |
| |
| if (attr->gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP) |
| return; |
| |
| if (!mlx5_is_macsec_roce_supported(dev->mdev)) { |
| mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n"); |
| return; |
| } |
| |
| mgids = &dev->port[attr->port_num - 1].reserved_gids[attr->index]; |
| if (mgids->macsec_index != -1) { /* Checking if physical gid has ambiguous IP */ |
| rdma_put_gid_attr(mgids->physical_gid); |
| mgids->macsec_index = -1; |
| return; |
| } |
| |
| rcu_read_lock(); |
| ndev = rcu_dereference(attr->ndev); |
| if (!ndev) { |
| rcu_read_unlock(); |
| return; |
| } |
| |
| if (!netif_is_macsec(ndev) || !macsec_netdev_is_offloaded(ndev)) { |
| rcu_read_unlock(); |
| return; |
| } |
| dev_hold(ndev); |
| rcu_read_unlock(); |
| |
| mutex_lock(&dev->macsec.lock); |
| max_gids = MLX5_CAP_ROCE(dev->mdev, roce_address_table_size); |
| for (i = 0; i < max_gids; i++) { /* Checking if macsec gid has ambiguous IP */ |
| mgids = &dev->port[attr->port_num - 1].reserved_gids[i]; |
| if (mgids->macsec_index == attr->index) { |
| const struct ib_gid_attr *physical_gid = mgids->physical_gid; |
| |
| set_roce_addr(to_mdev(physical_gid->device), |
| physical_gid->port_num, |
| physical_gid->index, |
| &physical_gid->gid, physical_gid); |
| |
| rdma_put_gid_attr(physical_gid); |
| mgids->macsec_index = -1; |
| break; |
| } |
| } |
| macsec_device = get_macsec_device(ndev, &dev->macsec.macsec_devices_list); |
| mlx5_macsec_del_roce_rule(attr->index, dev->mdev->macsec_fs, |
| &macsec_device->tx_rules_list, &macsec_device->rx_rules_list); |
| mlx5_macsec_del_roce_gid(macsec_device, attr->index); |
| cleanup_macsec_device(macsec_device); |
| |
| dev_put(ndev); |
| mutex_unlock(&dev->macsec.lock); |
| } |