Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/marvell/prestera/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 11 kB image not shown  

Quelle  prestera_counter.c   Sprache: C

 
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2021 Marvell International Ltd. All rights reserved */

#include "prestera.h"
#include "prestera_hw.h"
#include "prestera_acl.h"
#include "prestera_counter.h"

#define COUNTER_POLL_TIME (msecs_to_jiffies(1000))
#define COUNTER_RESCHED_TIME (msecs_to_jiffies(50))
#define COUNTER_BULK_SIZE (256)

struct prestera_counter {
 struct prestera_switch *sw;
 struct delayed_work stats_dw;
 struct mutex mtx;  /* protect block_list */
 struct prestera_counter_block **block_list;
 u32 total_read;
 u32 block_list_len;
 u32 curr_idx;
 bool is_fetching;
};

struct prestera_counter_block {
 struct list_head list;
 u32 id;
 u32 offset;
 u32 num_counters;
 u32 client;
 struct idr counter_idr;
 refcount_t refcnt;
 struct mutex mtx;  /* protect stats and counter_idr */
 struct prestera_counter_stats *stats;
 u8 *counter_flag;
 bool is_updating;
 bool full;
};

enum {
 COUNTER_FLAG_READY = 0,
 COUNTER_FLAG_INVALID = 1
};

static bool
prestera_counter_is_ready(struct prestera_counter_block *block, u32 id)
{
 return block->counter_flag[id - block->offset] == COUNTER_FLAG_READY;
}

static void prestera_counter_lock(struct prestera_counter *counter)
{
 mutex_lock(&counter->mtx);
}

static void prestera_counter_unlock(struct prestera_counter *counter)
{
 mutex_unlock(&counter->mtx);
}

static void prestera_counter_block_lock(struct prestera_counter_block *block)
{
 mutex_lock(&block->mtx);
}

static void prestera_counter_block_unlock(struct prestera_counter_block *block)
{
 mutex_unlock(&block->mtx);
}

static bool prestera_counter_block_incref(struct prestera_counter_block *block)
{
 return refcount_inc_not_zero(&block->refcnt);
}

static bool prestera_counter_block_decref(struct prestera_counter_block *block)
{
 return refcount_dec_and_test(&block->refcnt);
}

/* must be called with prestera_counter_block_lock() */
static void prestera_counter_stats_clear(struct prestera_counter_block *block,
      u32 counter_id)
{
 memset(&block->stats[counter_id - block->offset], 0,
        sizeof(*block->stats));
}

static struct prestera_counter_block *
prestera_counter_block_lookup_not_full(struct prestera_counter *counter,
           u32 client)
{
 u32 i;

 prestera_counter_lock(counter);
 for (i = 0; i < counter->block_list_len; i++) {
  if (counter->block_list[i] &&
      counter->block_list[i]->client == client &&
      !counter->block_list[i]->full &&
      prestera_counter_block_incref(counter->block_list[i])) {
   prestera_counter_unlock(counter);
   return counter->block_list[i];
  }
 }
 prestera_counter_unlock(counter);

 return NULL;
}

static int prestera_counter_block_list_add(struct prestera_counter *counter,
        struct prestera_counter_block *block)
{
 struct prestera_counter_block **arr;
 u32 i;

 prestera_counter_lock(counter);

 for (i = 0; i < counter->block_list_len; i++) {
  if (counter->block_list[i])
   continue;

  counter->block_list[i] = block;
  prestera_counter_unlock(counter);
  return 0;
 }

 arr = krealloc(counter->block_list, (counter->block_list_len + 1) *
         sizeof(*counter->block_list), GFP_KERNEL);
 if (!arr) {
  prestera_counter_unlock(counter);
  return -ENOMEM;
 }

 counter->block_list = arr;
 counter->block_list[counter->block_list_len] = block;
 counter->block_list_len++;
 prestera_counter_unlock(counter);
 return 0;
}

static struct prestera_counter_block *
prestera_counter_block_get(struct prestera_counter *counter, u32 client)
{
 struct prestera_counter_block *block;
 int err;

 block = prestera_counter_block_lookup_not_full(counter, client);
 if (block)
  return block;

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

 err = prestera_hw_counter_block_get(counter->sw, client,
         &block->id, &block->offset,
         &block->num_counters);
 if (err)
  goto err_block;

 block->stats = kcalloc(block->num_counters,
          sizeof(*block->stats), GFP_KERNEL);
 if (!block->stats) {
  err = -ENOMEM;
  goto err_stats;
 }

 block->counter_flag = kcalloc(block->num_counters,
          sizeof(*block->counter_flag),
          GFP_KERNEL);
 if (!block->counter_flag) {
  err = -ENOMEM;
  goto err_flag;
 }

 block->client = client;
 mutex_init(&block->mtx);
 refcount_set(&block->refcnt, 1);
 idr_init_base(&block->counter_idr, block->offset);

 err = prestera_counter_block_list_add(counter, block);
 if (err)
  goto err_list_add;

 return block;

err_list_add:
 idr_destroy(&block->counter_idr);
 mutex_destroy(&block->mtx);
 kfree(block->counter_flag);
err_flag:
 kfree(block->stats);
err_stats:
 prestera_hw_counter_block_release(counter->sw, block->id);
err_block:
 kfree(block);
 return ERR_PTR(err);
}

static void prestera_counter_block_put(struct prestera_counter *counter,
           struct prestera_counter_block *block)
{
 u32 i;

 if (!prestera_counter_block_decref(block))
  return;

 prestera_counter_lock(counter);
 for (i = 0; i < counter->block_list_len; i++) {
  if (counter->block_list[i] &&
      counter->block_list[i]->id == block->id) {
   counter->block_list[i] = NULL;
   break;
  }
 }
 prestera_counter_unlock(counter);

 WARN_ON(!idr_is_empty(&block->counter_idr));

 prestera_hw_counter_block_release(counter->sw, block->id);
 idr_destroy(&block->counter_idr);
 mutex_destroy(&block->mtx);
 kfree(block->stats);
 kfree(block);
}

static int prestera_counter_get_vacant(struct prestera_counter_block *block,
           u32 *id)
{
 int free_id;

 if (block->full)
  return -ENOSPC;

 prestera_counter_block_lock(block);
 free_id = idr_alloc_cyclic(&block->counter_idr, NULL, block->offset,
       block->offset + block->num_counters,
       GFP_KERNEL);
 if (free_id < 0) {
  if (free_id == -ENOSPC)
   block->full = true;

  prestera_counter_block_unlock(block);
  return free_id;
 }
 *id = free_id;
 prestera_counter_block_unlock(block);

 return 0;
}

int prestera_counter_get(struct prestera_counter *counter, u32 client,
    struct prestera_counter_block **bl, u32 *counter_id)
{
 struct prestera_counter_block *block;
 int err;
 u32 id;

get_next_block:
 block = prestera_counter_block_get(counter, client);
 if (IS_ERR(block))
  return PTR_ERR(block);

 err = prestera_counter_get_vacant(block, &id);
 if (err) {
  prestera_counter_block_put(counter, block);

  if (err == -ENOSPC)
   goto get_next_block;

  return err;
 }

 prestera_counter_block_lock(block);
 if (block->is_updating)
  block->counter_flag[id - block->offset] = COUNTER_FLAG_INVALID;
 prestera_counter_block_unlock(block);

 *counter_id = id;
 *bl = block;

 return 0;
}

void prestera_counter_put(struct prestera_counter *counter,
     struct prestera_counter_block *block, u32 counter_id)
{
 if (!block)
  return;

 prestera_counter_block_lock(block);
 idr_remove(&block->counter_idr, counter_id);
 block->full = false;
 prestera_counter_stats_clear(block, counter_id);
 prestera_counter_block_unlock(block);

 prestera_hw_counter_clear(counter->sw, block->id, counter_id);
 prestera_counter_block_put(counter, block);
}

static u32 prestera_counter_block_idx_next(struct prestera_counter *counter,
        u32 curr_idx)
{
 u32 idx, i, start = curr_idx + 1;

 prestera_counter_lock(counter);
 for (i = 0; i < counter->block_list_len; i++) {
  idx = (start + i) % counter->block_list_len;
  if (!counter->block_list[idx])
   continue;

  prestera_counter_unlock(counter);
  return idx;
 }
 prestera_counter_unlock(counter);

 return 0;
}

static struct prestera_counter_block *
prestera_counter_block_get_by_idx(struct prestera_counter *counter, u32 idx)
{
 if (idx >= counter->block_list_len)
  return NULL;

 prestera_counter_lock(counter);

 if (!counter->block_list[idx] ||
     !prestera_counter_block_incref(counter->block_list[idx])) {
  prestera_counter_unlock(counter);
  return NULL;
 }

 prestera_counter_unlock(counter);
 return counter->block_list[idx];
}

static void prestera_counter_stats_work(struct work_struct *work)
{
 struct delayed_work *dl_work = to_delayed_work(work);
 struct prestera_counter *counter =
  container_of(dl_work, struct prestera_counter, stats_dw);
 struct prestera_counter_block *block;
 u32 resched_time = COUNTER_POLL_TIME;
 u32 count = COUNTER_BULK_SIZE;
 bool done = false;
 int err;
 u32 i;

 block = prestera_counter_block_get_by_idx(counter, counter->curr_idx);
 if (!block) {
  if (counter->is_fetching)
   goto abort;

  goto next;
 }

 if (!counter->is_fetching) {
  err = prestera_hw_counter_trigger(counter->sw, block->id);
  if (err)
   goto abort;

  prestera_counter_block_lock(block);
  block->is_updating = true;
  prestera_counter_block_unlock(block);

  counter->is_fetching = true;
  counter->total_read = 0;
  resched_time = COUNTER_RESCHED_TIME;
  goto resched;
 }

 prestera_counter_block_lock(block);
 err = prestera_hw_counters_get(counter->sw, counter->total_read,
           &count, &done,
           &block->stats[counter->total_read]);
 prestera_counter_block_unlock(block);
 if (err)
  goto abort;

 counter->total_read += count;
 if (!done || counter->total_read < block->num_counters) {
  resched_time = COUNTER_RESCHED_TIME;
  goto resched;
 }

 for (i = 0; i < block->num_counters; i++) {
  if (block->counter_flag[i] == COUNTER_FLAG_INVALID) {
   prestera_counter_block_lock(block);
   block->counter_flag[i] = COUNTER_FLAG_READY;
   memset(&block->stats[i], 0, sizeof(*block->stats));
   prestera_counter_block_unlock(block);
  }
 }

 prestera_counter_block_lock(block);
 block->is_updating = false;
 prestera_counter_block_unlock(block);

 goto next;
abort:
 prestera_hw_counter_abort(counter->sw);
next:
 counter->is_fetching = false;
 counter->curr_idx =
  prestera_counter_block_idx_next(counter, counter->curr_idx);
resched:
 if (block)
  prestera_counter_block_put(counter, block);

 schedule_delayed_work(&counter->stats_dw, resched_time);
}

/* Can be executed without rtnl_lock().
 * So pay attention when something changing.
 */

int prestera_counter_stats_get(struct prestera_counter *counter,
          struct prestera_counter_block *block,
          u32 counter_id, u64 *packets, u64 *bytes)
{
 if (!block || !prestera_counter_is_ready(block, counter_id)) {
  *packets = 0;
  *bytes = 0;
  return 0;
 }

 prestera_counter_block_lock(block);
 *packets = block->stats[counter_id - block->offset].packets;
 *bytes = block->stats[counter_id - block->offset].bytes;

 prestera_counter_stats_clear(block, counter_id);
 prestera_counter_block_unlock(block);

 return 0;
}

int prestera_counter_init(struct prestera_switch *sw)
{
 struct prestera_counter *counter;

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

 counter->block_list = kzalloc(sizeof(*counter->block_list), GFP_KERNEL);
 if (!counter->block_list) {
  kfree(counter);
  return -ENOMEM;
 }

 mutex_init(&counter->mtx);
 counter->block_list_len = 1;
 counter->sw = sw;
 sw->counter = counter;

 INIT_DELAYED_WORK(&counter->stats_dw, prestera_counter_stats_work);
 schedule_delayed_work(&counter->stats_dw, COUNTER_POLL_TIME);

 return 0;
}

void prestera_counter_fini(struct prestera_switch *sw)
{
 struct prestera_counter *counter = sw->counter;
 u32 i;

 cancel_delayed_work_sync(&counter->stats_dw);

 for (i = 0; i < counter->block_list_len; i++)
  WARN_ON(counter->block_list[i]);

 mutex_destroy(&counter->mtx);
 kfree(counter->block_list);
 kfree(counter);
}

Messung V0.5
C=96 H=88 G=91

¤ Dauer der Verarbeitung: 0.13 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.