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 #include "mlx5_core.h" 7 8 static LIST_HEAD(devcom_list); 9 10 #define devcom_for_each_component(priv, comp, iter) \ 11 for (iter = 0; \ 12 comp = &(priv)->components[iter], iter < MLX5_DEVCOM_NUM_COMPONENTS; \ 13 iter++) 14 15 struct mlx5_devcom_component { 16 struct { 17 void __rcu *data; 18 } device[MLX5_DEVCOM_PORTS_SUPPORTED]; 19 20 mlx5_devcom_event_handler_t handler; 21 struct rw_semaphore sem; 22 bool paired; 23 }; 24 25 struct mlx5_devcom_list { 26 struct list_head list; 27 28 struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS]; 29 struct mlx5_core_dev *devs[MLX5_DEVCOM_PORTS_SUPPORTED]; 30 }; 31 32 struct mlx5_devcom { 33 struct mlx5_devcom_list *priv; 34 int idx; 35 }; 36 37 static struct mlx5_devcom_list *mlx5_devcom_list_alloc(void) 38 { 39 struct mlx5_devcom_component *comp; 40 struct mlx5_devcom_list *priv; 41 int i; 42 43 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 44 if (!priv) 45 return NULL; 46 47 devcom_for_each_component(priv, comp, i) 48 init_rwsem(&comp->sem); 49 50 return priv; 51 } 52 53 static struct mlx5_devcom *mlx5_devcom_alloc(struct mlx5_devcom_list *priv, 54 u8 idx) 55 { 56 struct mlx5_devcom *devcom; 57 58 devcom = kzalloc(sizeof(*devcom), GFP_KERNEL); 59 if (!devcom) 60 return NULL; 61 62 devcom->priv = priv; 63 devcom->idx = idx; 64 return devcom; 65 } 66 67 /* Must be called with intf_mutex held */ 68 struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev) 69 { 70 struct mlx5_devcom_list *priv = NULL, *iter; 71 struct mlx5_devcom *devcom = NULL; 72 bool new_priv = false; 73 u64 sguid0, sguid1; 74 int idx, i; 75 76 if (!mlx5_core_is_pf(dev)) 77 return NULL; 78 if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED) 79 return NULL; 80 81 mlx5_dev_list_lock(); 82 sguid0 = mlx5_query_nic_system_image_guid(dev); 83 list_for_each_entry(iter, &devcom_list, list) { 84 struct mlx5_core_dev *tmp_dev = NULL; 85 86 idx = -1; 87 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) { 88 if (iter->devs[i]) 89 tmp_dev = iter->devs[i]; 90 else 91 idx = i; 92 } 93 94 if (idx == -1) 95 continue; 96 97 sguid1 = mlx5_query_nic_system_image_guid(tmp_dev); 98 if (sguid0 != sguid1) 99 continue; 100 101 priv = iter; 102 break; 103 } 104 105 if (!priv) { 106 priv = mlx5_devcom_list_alloc(); 107 if (!priv) { 108 devcom = ERR_PTR(-ENOMEM); 109 goto out; 110 } 111 112 idx = 0; 113 new_priv = true; 114 } 115 116 priv->devs[idx] = dev; 117 devcom = mlx5_devcom_alloc(priv, idx); 118 if (!devcom) { 119 if (new_priv) 120 kfree(priv); 121 devcom = ERR_PTR(-ENOMEM); 122 goto out; 123 } 124 125 if (new_priv) 126 list_add(&priv->list, &devcom_list); 127 out: 128 mlx5_dev_list_unlock(); 129 return devcom; 130 } 131 132 /* Must be called with intf_mutex held */ 133 void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom) 134 { 135 struct mlx5_devcom_list *priv; 136 int i; 137 138 if (IS_ERR_OR_NULL(devcom)) 139 return; 140 141 mlx5_dev_list_lock(); 142 priv = devcom->priv; 143 priv->devs[devcom->idx] = NULL; 144 145 kfree(devcom); 146 147 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) 148 if (priv->devs[i]) 149 break; 150 151 if (i != MLX5_DEVCOM_PORTS_SUPPORTED) 152 goto out; 153 154 list_del(&priv->list); 155 kfree(priv); 156 out: 157 mlx5_dev_list_unlock(); 158 } 159 160 void mlx5_devcom_register_component(struct mlx5_devcom *devcom, 161 enum mlx5_devcom_components id, 162 mlx5_devcom_event_handler_t handler, 163 void *data) 164 { 165 struct mlx5_devcom_component *comp; 166 167 if (IS_ERR_OR_NULL(devcom)) 168 return; 169 170 WARN_ON(!data); 171 172 comp = &devcom->priv->components[id]; 173 down_write(&comp->sem); 174 comp->handler = handler; 175 rcu_assign_pointer(comp->device[devcom->idx].data, data); 176 up_write(&comp->sem); 177 } 178 179 void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom, 180 enum mlx5_devcom_components id) 181 { 182 struct mlx5_devcom_component *comp; 183 184 if (IS_ERR_OR_NULL(devcom)) 185 return; 186 187 comp = &devcom->priv->components[id]; 188 down_write(&comp->sem); 189 RCU_INIT_POINTER(comp->device[devcom->idx].data, NULL); 190 up_write(&comp->sem); 191 synchronize_rcu(); 192 } 193 194 int mlx5_devcom_send_event(struct mlx5_devcom *devcom, 195 enum mlx5_devcom_components id, 196 int event, 197 void *event_data) 198 { 199 struct mlx5_devcom_component *comp; 200 int err = -ENODEV, i; 201 202 if (IS_ERR_OR_NULL(devcom)) 203 return err; 204 205 comp = &devcom->priv->components[id]; 206 down_write(&comp->sem); 207 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) { 208 void *data = rcu_dereference_protected(comp->device[i].data, 209 lockdep_is_held(&comp->sem)); 210 211 if (i != devcom->idx && data) { 212 err = comp->handler(event, data, event_data); 213 break; 214 } 215 } 216 217 up_write(&comp->sem); 218 return err; 219 } 220 221 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom, 222 enum mlx5_devcom_components id, 223 bool paired) 224 { 225 struct mlx5_devcom_component *comp; 226 227 comp = &devcom->priv->components[id]; 228 WARN_ON(!rwsem_is_locked(&comp->sem)); 229 230 WRITE_ONCE(comp->paired, paired); 231 } 232 233 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom, 234 enum mlx5_devcom_components id) 235 { 236 if (IS_ERR_OR_NULL(devcom)) 237 return false; 238 239 return READ_ONCE(devcom->priv->components[id].paired); 240 } 241 242 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom, 243 enum mlx5_devcom_components id) 244 { 245 struct mlx5_devcom_component *comp; 246 int i; 247 248 if (IS_ERR_OR_NULL(devcom)) 249 return NULL; 250 251 comp = &devcom->priv->components[id]; 252 down_read(&comp->sem); 253 if (!READ_ONCE(comp->paired)) { 254 up_read(&comp->sem); 255 return NULL; 256 } 257 258 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) 259 if (i != devcom->idx) 260 break; 261 262 return rcu_dereference_protected(comp->device[i].data, lockdep_is_held(&comp->sem)); 263 } 264 265 void *mlx5_devcom_get_peer_data_rcu(struct mlx5_devcom *devcom, enum mlx5_devcom_components id) 266 { 267 struct mlx5_devcom_component *comp; 268 int i; 269 270 if (IS_ERR_OR_NULL(devcom)) 271 return NULL; 272 273 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) 274 if (i != devcom->idx) 275 break; 276 277 comp = &devcom->priv->components[id]; 278 /* This can change concurrently, however 'data' pointer will remain 279 * valid for the duration of RCU read section. 280 */ 281 if (!READ_ONCE(comp->paired)) 282 return NULL; 283 284 return rcu_dereference(comp->device[i].data); 285 } 286 287 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom, 288 enum mlx5_devcom_components id) 289 { 290 struct mlx5_devcom_component *comp = &devcom->priv->components[id]; 291 292 up_read(&comp->sem); 293 } 294