devcom.c (5284B)
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2/* Copyright (c) 2018 Mellanox Technologies */ 3 4#include <linux/mlx5/vport.h> 5#include "lib/devcom.h" 6 7static LIST_HEAD(devcom_list); 8 9#define devcom_for_each_component(priv, comp, iter) \ 10 for (iter = 0; \ 11 comp = &(priv)->components[iter], iter < MLX5_DEVCOM_NUM_COMPONENTS; \ 12 iter++) 13 14struct mlx5_devcom_component { 15 struct { 16 void *data; 17 } device[MLX5_DEVCOM_PORTS_SUPPORTED]; 18 19 mlx5_devcom_event_handler_t handler; 20 struct rw_semaphore sem; 21 bool paired; 22}; 23 24struct mlx5_devcom_list { 25 struct list_head list; 26 27 struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS]; 28 struct mlx5_core_dev *devs[MLX5_DEVCOM_PORTS_SUPPORTED]; 29}; 30 31struct mlx5_devcom { 32 struct mlx5_devcom_list *priv; 33 int idx; 34}; 35 36static struct mlx5_devcom_list *mlx5_devcom_list_alloc(void) 37{ 38 struct mlx5_devcom_component *comp; 39 struct mlx5_devcom_list *priv; 40 int i; 41 42 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 43 if (!priv) 44 return NULL; 45 46 devcom_for_each_component(priv, comp, i) 47 init_rwsem(&comp->sem); 48 49 return priv; 50} 51 52static struct mlx5_devcom *mlx5_devcom_alloc(struct mlx5_devcom_list *priv, 53 u8 idx) 54{ 55 struct mlx5_devcom *devcom; 56 57 devcom = kzalloc(sizeof(*devcom), GFP_KERNEL); 58 if (!devcom) 59 return NULL; 60 61 devcom->priv = priv; 62 devcom->idx = idx; 63 return devcom; 64} 65 66/* Must be called with intf_mutex held */ 67struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev) 68{ 69 struct mlx5_devcom_list *priv = NULL, *iter; 70 struct mlx5_devcom *devcom = NULL; 71 bool new_priv = false; 72 u64 sguid0, sguid1; 73 int idx, i; 74 75 if (!mlx5_core_is_pf(dev)) 76 return NULL; 77 if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED) 78 return NULL; 79 80 sguid0 = mlx5_query_nic_system_image_guid(dev); 81 list_for_each_entry(iter, &devcom_list, list) { 82 struct mlx5_core_dev *tmp_dev = NULL; 83 84 idx = -1; 85 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) { 86 if (iter->devs[i]) 87 tmp_dev = iter->devs[i]; 88 else 89 idx = i; 90 } 91 92 if (idx == -1) 93 continue; 94 95 sguid1 = mlx5_query_nic_system_image_guid(tmp_dev); 96 if (sguid0 != sguid1) 97 continue; 98 99 priv = iter; 100 break; 101 } 102 103 if (!priv) { 104 priv = mlx5_devcom_list_alloc(); 105 if (!priv) 106 return ERR_PTR(-ENOMEM); 107 108 idx = 0; 109 new_priv = true; 110 } 111 112 priv->devs[idx] = dev; 113 devcom = mlx5_devcom_alloc(priv, idx); 114 if (!devcom) { 115 kfree(priv); 116 return ERR_PTR(-ENOMEM); 117 } 118 119 if (new_priv) 120 list_add(&priv->list, &devcom_list); 121 122 return devcom; 123} 124 125/* Must be called with intf_mutex held */ 126void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom) 127{ 128 struct mlx5_devcom_list *priv; 129 int i; 130 131 if (IS_ERR_OR_NULL(devcom)) 132 return; 133 134 priv = devcom->priv; 135 priv->devs[devcom->idx] = NULL; 136 137 kfree(devcom); 138 139 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) 140 if (priv->devs[i]) 141 break; 142 143 if (i != MLX5_DEVCOM_PORTS_SUPPORTED) 144 return; 145 146 list_del(&priv->list); 147 kfree(priv); 148} 149 150void mlx5_devcom_register_component(struct mlx5_devcom *devcom, 151 enum mlx5_devcom_components id, 152 mlx5_devcom_event_handler_t handler, 153 void *data) 154{ 155 struct mlx5_devcom_component *comp; 156 157 if (IS_ERR_OR_NULL(devcom)) 158 return; 159 160 WARN_ON(!data); 161 162 comp = &devcom->priv->components[id]; 163 down_write(&comp->sem); 164 comp->handler = handler; 165 comp->device[devcom->idx].data = data; 166 up_write(&comp->sem); 167} 168 169void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom, 170 enum mlx5_devcom_components id) 171{ 172 struct mlx5_devcom_component *comp; 173 174 if (IS_ERR_OR_NULL(devcom)) 175 return; 176 177 comp = &devcom->priv->components[id]; 178 down_write(&comp->sem); 179 comp->device[devcom->idx].data = NULL; 180 up_write(&comp->sem); 181} 182 183int mlx5_devcom_send_event(struct mlx5_devcom *devcom, 184 enum mlx5_devcom_components id, 185 int event, 186 void *event_data) 187{ 188 struct mlx5_devcom_component *comp; 189 int err = -ENODEV, i; 190 191 if (IS_ERR_OR_NULL(devcom)) 192 return err; 193 194 comp = &devcom->priv->components[id]; 195 down_write(&comp->sem); 196 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) 197 if (i != devcom->idx && comp->device[i].data) { 198 err = comp->handler(event, comp->device[i].data, 199 event_data); 200 break; 201 } 202 203 up_write(&comp->sem); 204 return err; 205} 206 207void mlx5_devcom_set_paired(struct mlx5_devcom *devcom, 208 enum mlx5_devcom_components id, 209 bool paired) 210{ 211 struct mlx5_devcom_component *comp; 212 213 comp = &devcom->priv->components[id]; 214 WARN_ON(!rwsem_is_locked(&comp->sem)); 215 216 comp->paired = paired; 217} 218 219bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom, 220 enum mlx5_devcom_components id) 221{ 222 if (IS_ERR_OR_NULL(devcom)) 223 return false; 224 225 return devcom->priv->components[id].paired; 226} 227 228void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom, 229 enum mlx5_devcom_components id) 230{ 231 struct mlx5_devcom_component *comp; 232 int i; 233 234 if (IS_ERR_OR_NULL(devcom)) 235 return NULL; 236 237 comp = &devcom->priv->components[id]; 238 down_read(&comp->sem); 239 if (!comp->paired) { 240 up_read(&comp->sem); 241 return NULL; 242 } 243 244 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) 245 if (i != devcom->idx) 246 break; 247 248 return comp->device[i].data; 249} 250 251void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom, 252 enum mlx5_devcom_components id) 253{ 254 struct mlx5_devcom_component *comp = &devcom->priv->components[id]; 255 256 up_read(&comp->sem); 257}