Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/ethernet/mellanox/mlx5/core/lib/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 9 kB image not shown  

Quelle  devcom.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2018 Mellanox Technologies */

#include <linux/mlx5/vport.h>
#include <linux/list.h>
#include "lib/devcom.h"
#include "mlx5_core.h"

static LIST_HEAD(devcom_dev_list);
static LIST_HEAD(devcom_comp_list);
/* protect device list */
static DEFINE_MUTEX(dev_list_lock);
/* protect component list */
static DEFINE_MUTEX(comp_list_lock);

#define devcom_for_each_component(iter) \
 list_for_each_entry(iter, &devcom_comp_list, comp_list)

struct mlx5_devcom_dev {
 struct list_head list;
 struct mlx5_core_dev *dev;
 struct kref ref;
};

struct mlx5_devcom_comp {
 struct list_head comp_list;
 enum mlx5_devcom_component id;
 u64 key;
 struct list_head comp_dev_list_head;
 mlx5_devcom_event_handler_t handler;
 struct kref ref;
 bool ready;
 struct rw_semaphore sem;
 struct lock_class_key lock_key;
};

struct mlx5_devcom_comp_dev {
 struct list_head list;
 struct mlx5_devcom_comp *comp;
 struct mlx5_devcom_dev *devc;
 void __rcu *data;
};

static bool devcom_dev_exists(struct mlx5_core_dev *dev)
{
 struct mlx5_devcom_dev *iter;

 list_for_each_entry(iter, &devcom_dev_list, list)
  if (iter->dev == dev)
   return true;

 return false;
}

static struct mlx5_devcom_dev *
mlx5_devcom_dev_alloc(struct mlx5_core_dev *dev)
{
 struct mlx5_devcom_dev *devc;

 devc = kzalloc(sizeof(*devc), GFP_KERNEL);
 if (!devc)
  return NULL;

 devc->dev = dev;
 kref_init(&devc->ref);
 return devc;
}

struct mlx5_devcom_dev *
mlx5_devcom_register_device(struct mlx5_core_dev *dev)
{
 struct mlx5_devcom_dev *devc;

 mutex_lock(&dev_list_lock);

 if (devcom_dev_exists(dev)) {
  devc = ERR_PTR(-EEXIST);
  goto out;
 }

 devc = mlx5_devcom_dev_alloc(dev);
 if (!devc) {
  devc = ERR_PTR(-ENOMEM);
  goto out;
 }

 list_add_tail(&devc->list, &devcom_dev_list);
out:
 mutex_unlock(&dev_list_lock);
 return devc;
}

static void
mlx5_devcom_dev_release(struct kref *ref)
{
 struct mlx5_devcom_dev *devc = container_of(ref, struct mlx5_devcom_dev, ref);

 mutex_lock(&dev_list_lock);
 list_del(&devc->list);
 mutex_unlock(&dev_list_lock);
 kfree(devc);
}

void mlx5_devcom_unregister_device(struct mlx5_devcom_dev *devc)
{
 if (!IS_ERR_OR_NULL(devc))
  kref_put(&devc->ref, mlx5_devcom_dev_release);
}

static struct mlx5_devcom_comp *
mlx5_devcom_comp_alloc(u64 id, u64 key, mlx5_devcom_event_handler_t handler)
{
 struct mlx5_devcom_comp *comp;

 comp = kzalloc(sizeof(*comp), GFP_KERNEL);
 if (!comp)
  return ERR_PTR(-ENOMEM);

 comp->id = id;
 comp->key = key;
 comp->handler = handler;
 init_rwsem(&comp->sem);
 lockdep_register_key(&comp->lock_key);
 lockdep_set_class(&comp->sem, &comp->lock_key);
 kref_init(&comp->ref);
 INIT_LIST_HEAD(&comp->comp_dev_list_head);

 return comp;
}

static void
mlx5_devcom_comp_release(struct kref *ref)
{
 struct mlx5_devcom_comp *comp = container_of(ref, struct mlx5_devcom_comp, ref);

 mutex_lock(&comp_list_lock);
 list_del(&comp->comp_list);
 mutex_unlock(&comp_list_lock);
 lockdep_unregister_key(&comp->lock_key);
 kfree(comp);
}

static struct mlx5_devcom_comp_dev *
devcom_alloc_comp_dev(struct mlx5_devcom_dev *devc,
        struct mlx5_devcom_comp *comp,
        void *data)
{
 struct mlx5_devcom_comp_dev *devcom;

 devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
 if (!devcom)
  return ERR_PTR(-ENOMEM);

 kref_get(&devc->ref);
 devcom->devc = devc;
 devcom->comp = comp;
 rcu_assign_pointer(devcom->data, data);

 down_write(&comp->sem);
 list_add_tail(&devcom->list, &comp->comp_dev_list_head);
 up_write(&comp->sem);

 return devcom;
}

static void
devcom_free_comp_dev(struct mlx5_devcom_comp_dev *devcom)
{
 struct mlx5_devcom_comp *comp = devcom->comp;

 down_write(&comp->sem);
 list_del(&devcom->list);
 up_write(&comp->sem);

 kref_put(&devcom->devc->ref, mlx5_devcom_dev_release);
 kfree(devcom);
 kref_put(&comp->ref, mlx5_devcom_comp_release);
}

static bool
devcom_component_equal(struct mlx5_devcom_comp *devcom,
         enum mlx5_devcom_component id,
         u64 key)
{
 return devcom->id == id && devcom->key == key;
}

static struct mlx5_devcom_comp *
devcom_component_get(struct mlx5_devcom_dev *devc,
       enum mlx5_devcom_component id,
       u64 key,
       mlx5_devcom_event_handler_t handler)
{
 struct mlx5_devcom_comp *comp;

 devcom_for_each_component(comp) {
  if (devcom_component_equal(comp, id, key)) {
   if (handler == comp->handler) {
    kref_get(&comp->ref);
    return comp;
   }

   mlx5_core_err(devc->dev,
          "Cannot register existing devcom component with different handler\n");
   return ERR_PTR(-EINVAL);
  }
 }

 return NULL;
}

struct mlx5_devcom_comp_dev *
mlx5_devcom_register_component(struct mlx5_devcom_dev *devc,
          enum mlx5_devcom_component id,
          u64 key,
          mlx5_devcom_event_handler_t handler,
          void *data)
{
 struct mlx5_devcom_comp_dev *devcom;
 struct mlx5_devcom_comp *comp;

 if (IS_ERR_OR_NULL(devc))
  return ERR_PTR(-EINVAL);

 mutex_lock(&comp_list_lock);
 comp = devcom_component_get(devc, id, key, handler);
 if (IS_ERR(comp)) {
  devcom = ERR_PTR(-EINVAL);
  goto out_unlock;
 }

 if (!comp) {
  comp = mlx5_devcom_comp_alloc(id, key, handler);
  if (IS_ERR(comp)) {
   devcom = ERR_CAST(comp);
   goto out_unlock;
  }
  list_add_tail(&comp->comp_list, &devcom_comp_list);
 }
 mutex_unlock(&comp_list_lock);

 devcom = devcom_alloc_comp_dev(devc, comp, data);
 if (IS_ERR(devcom))
  kref_put(&comp->ref, mlx5_devcom_comp_release);

 return devcom;

out_unlock:
 mutex_unlock(&comp_list_lock);
 return devcom;
}

void mlx5_devcom_unregister_component(struct mlx5_devcom_comp_dev *devcom)
{
 if (!IS_ERR_OR_NULL(devcom))
  devcom_free_comp_dev(devcom);
}

int mlx5_devcom_comp_get_size(struct mlx5_devcom_comp_dev *devcom)
{
 struct mlx5_devcom_comp *comp = devcom->comp;

 return kref_read(&comp->ref);
}

int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
      int event, int rollback_event,
      void *event_data)
{
 struct mlx5_devcom_comp_dev *pos;
 struct mlx5_devcom_comp *comp;
 int err = 0;
 void *data;

 if (IS_ERR_OR_NULL(devcom))
  return -ENODEV;

 comp = devcom->comp;
 down_write(&comp->sem);
 list_for_each_entry(pos, &comp->comp_dev_list_head, list) {
  data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));

  if (pos != devcom && data) {
   err = comp->handler(event, data, event_data);
   if (err)
    goto rollback;
  }
 }

 up_write(&comp->sem);
 return 0;

rollback:
 if (list_entry_is_head(pos, &comp->comp_dev_list_head, list))
  goto out;
 pos = list_prev_entry(pos, list);
 list_for_each_entry_from_reverse(pos, &comp->comp_dev_list_head, list) {
  data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));

  if (pos != devcom && data)
   comp->handler(rollback_event, data, event_data);
 }
out:
 up_write(&comp->sem);
 return err;
}

void mlx5_devcom_comp_set_ready(struct mlx5_devcom_comp_dev *devcom, bool ready)
{
 WARN_ON(!rwsem_is_locked(&devcom->comp->sem));

 WRITE_ONCE(devcom->comp->ready, ready);
}

bool mlx5_devcom_comp_is_ready(struct mlx5_devcom_comp_dev *devcom)
{
 if (IS_ERR_OR_NULL(devcom))
  return false;

 return READ_ONCE(devcom->comp->ready);
}

bool mlx5_devcom_for_each_peer_begin(struct mlx5_devcom_comp_dev *devcom)
{
 struct mlx5_devcom_comp *comp;

 if (IS_ERR_OR_NULL(devcom))
  return false;

 comp = devcom->comp;
 down_read(&comp->sem);
 if (!READ_ONCE(comp->ready)) {
  up_read(&comp->sem);
  return false;
 }

 return true;
}

void mlx5_devcom_for_each_peer_end(struct mlx5_devcom_comp_dev *devcom)
{
 up_read(&devcom->comp->sem);
}

void *mlx5_devcom_get_next_peer_data(struct mlx5_devcom_comp_dev *devcom,
         struct mlx5_devcom_comp_dev **pos)
{
 struct mlx5_devcom_comp *comp = devcom->comp;
 struct mlx5_devcom_comp_dev *tmp;
 void *data;

 tmp = list_prepare_entry(*pos, &comp->comp_dev_list_head, list);

 list_for_each_entry_continue(tmp, &comp->comp_dev_list_head, list) {
  if (tmp != devcom) {
   data = rcu_dereference_protected(tmp->data, lockdep_is_held(&comp->sem));
   if (data)
    break;
  }
 }

 if (list_entry_is_head(tmp, &comp->comp_dev_list_head, list))
  return NULL;

 *pos = tmp;
 return data;
}

void *mlx5_devcom_get_next_peer_data_rcu(struct mlx5_devcom_comp_dev *devcom,
      struct mlx5_devcom_comp_dev **pos)
{
 struct mlx5_devcom_comp *comp = devcom->comp;
 struct mlx5_devcom_comp_dev *tmp;
 void *data;

 tmp = list_prepare_entry(*pos, &comp->comp_dev_list_head, list);

 list_for_each_entry_continue(tmp, &comp->comp_dev_list_head, list) {
  if (tmp != devcom) {
   /* This can change concurrently, however 'data' pointer will remain
 * valid for the duration of RCU read section.
 */

   if (!READ_ONCE(comp->ready))
    return NULL;
   data = rcu_dereference(tmp->data);
   if (data)
    break;
  }
 }

 if (list_entry_is_head(tmp, &comp->comp_dev_list_head, list))
  return NULL;

 *pos = tmp;
 return data;
}

void mlx5_devcom_comp_lock(struct mlx5_devcom_comp_dev *devcom)
{
 if (IS_ERR_OR_NULL(devcom))
  return;
 down_write(&devcom->comp->sem);
}

void mlx5_devcom_comp_unlock(struct mlx5_devcom_comp_dev *devcom)
{
 if (IS_ERR_OR_NULL(devcom))
  return;
 up_write(&devcom->comp->sem);
}

int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom)
{
 if (IS_ERR_OR_NULL(devcom))
  return 0;
 return down_write_trylock(&devcom->comp->sem);
}

Messung V0.5
C=99 H=97 G=97

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.