linux/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
<<
>>
Prefs
   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_MAX_PORTS];
  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_MAX_PORTS];
  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
  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 */
 124void 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
 148void 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
 167void 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
 181int 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
 205void 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
 217bool 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
 226void *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
 249void 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