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 7 static 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 14 struct mlx5_devcom_component { 15 struct { 16 void *data; 17 } device[MLX5_MAX_PORTS]; 18 19 mlx5_devcom_event_handler_t handler; 20 struct rw_semaphore sem; 21 bool paired; 22 }; 23 24 struct 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_MAX_PORTS]; 29 }; 30 31 struct mlx5_devcom { 32 struct mlx5_devcom_list *priv; 33 int idx; 34 }; 35 36 static 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 52 static 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 */ 67 struct 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 78 sguid0 = mlx5_query_nic_system_image_guid(dev); 79 list_for_each_entry(iter, &devcom_list, list) { 80 struct mlx5_core_dev *tmp_dev = NULL; 81 82 idx = -1; 83 for (i = 0; i < MLX5_MAX_PORTS; i++) { 84 if (iter->devs[i]) 85 tmp_dev = iter->devs[i]; 86 else 87 idx = i; 88 } 89 90 if (idx == -1) 91 continue; 92 93 sguid1 = mlx5_query_nic_system_image_guid(tmp_dev); 94 if (sguid0 != sguid1) 95 continue; 96 97 priv = iter; 98 break; 99 } 100 101 if (!priv) { 102 priv = mlx5_devcom_list_alloc(); 103 if (!priv) 104 return ERR_PTR(-ENOMEM); 105 106 idx = 0; 107 new_priv = true; 108 } 109 110 priv->devs[idx] = dev; 111 devcom = mlx5_devcom_alloc(priv, idx); 112 if (!devcom) { 113 kfree(priv); 114 return ERR_PTR(-ENOMEM); 115 } 116 117 if (new_priv) 118 list_add(&priv->list, &devcom_list); 119 120 return devcom; 121 } 122 123 /* Must be called with intf_mutex held */ 124 void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom) 125 { 126 struct mlx5_devcom_list *priv; 127 int i; 128 129 if (IS_ERR_OR_NULL(devcom)) 130 return; 131 132 priv = devcom->priv; 133 priv->devs[devcom->idx] = NULL; 134 135 kfree(devcom); 136 137 for (i = 0; i < MLX5_MAX_PORTS; i++) 138 if (priv->devs[i]) 139 break; 140 141 if (i != MLX5_MAX_PORTS) 142 return; 143 144 list_del(&priv->list); 145 kfree(priv); 146 } 147 148 void mlx5_devcom_register_component(struct mlx5_devcom *devcom, 149 enum mlx5_devcom_components id, 150 mlx5_devcom_event_handler_t handler, 151 void *data) 152 { 153 struct mlx5_devcom_component *comp; 154 155 if (IS_ERR_OR_NULL(devcom)) 156 return; 157 158 WARN_ON(!data); 159 160 comp = &devcom->priv->components[id]; 161 down_write(&comp->sem); 162 comp->handler = handler; 163 comp->device[devcom->idx].data = data; 164 up_write(&comp->sem); 165 } 166 167 void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom, 168 enum mlx5_devcom_components id) 169 { 170 struct mlx5_devcom_component *comp; 171 172 if (IS_ERR_OR_NULL(devcom)) 173 return; 174 175 comp = &devcom->priv->components[id]; 176 down_write(&comp->sem); 177 comp->device[devcom->idx].data = NULL; 178 up_write(&comp->sem); 179 } 180 181 int mlx5_devcom_send_event(struct mlx5_devcom *devcom, 182 enum mlx5_devcom_components id, 183 int event, 184 void *event_data) 185 { 186 struct mlx5_devcom_component *comp; 187 int err = -ENODEV, i; 188 189 if (IS_ERR_OR_NULL(devcom)) 190 return err; 191 192 comp = &devcom->priv->components[id]; 193 down_write(&comp->sem); 194 for (i = 0; i < MLX5_MAX_PORTS; i++) 195 if (i != devcom->idx && comp->device[i].data) { 196 err = comp->handler(event, comp->device[i].data, 197 event_data); 198 break; 199 } 200 201 up_write(&comp->sem); 202 return err; 203 } 204 205 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom, 206 enum mlx5_devcom_components id, 207 bool paired) 208 { 209 struct mlx5_devcom_component *comp; 210 211 comp = &devcom->priv->components[id]; 212 WARN_ON(!rwsem_is_locked(&comp->sem)); 213 214 comp->paired = paired; 215 } 216 217 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom, 218 enum mlx5_devcom_components id) 219 { 220 if (IS_ERR_OR_NULL(devcom)) 221 return false; 222 223 return devcom->priv->components[id].paired; 224 } 225 226 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom, 227 enum mlx5_devcom_components id) 228 { 229 struct mlx5_devcom_component *comp; 230 int i; 231 232 if (IS_ERR_OR_NULL(devcom)) 233 return NULL; 234 235 comp = &devcom->priv->components[id]; 236 down_read(&comp->sem); 237 if (!comp->paired) { 238 up_read(&comp->sem); 239 return NULL; 240 } 241 242 for (i = 0; i < MLX5_MAX_PORTS; i++) 243 if (i != devcom->idx) 244 break; 245 246 return comp->device[i].data; 247 } 248 249 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom, 250 enum mlx5_devcom_components id) 251 { 252 struct mlx5_devcom_component *comp = &devcom->priv->components[id]; 253 254 up_read(&comp->sem); 255 } 256