Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/ethernet/netronome/nfp/flower/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 18 kB image not shown  

Quelle  lag_conf.c   Sprache: C

 
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2018 Netronome Systems, Inc. */

#include "main.h"

/* LAG group config flags. */
#define NFP_FL_LAG_LAST   BIT(1)
#define NFP_FL_LAG_FIRST  BIT(2)
#define NFP_FL_LAG_DATA   BIT(3)
#define NFP_FL_LAG_XON   BIT(4)
#define NFP_FL_LAG_SYNC   BIT(5)
#define NFP_FL_LAG_SWITCH  BIT(6)
#define NFP_FL_LAG_RESET  BIT(7)

/* LAG port state flags. */
#define NFP_PORT_LAG_LINK_UP  BIT(0)
#define NFP_PORT_LAG_TX_ENABLED  BIT(1)
#define NFP_PORT_LAG_CHANGED  BIT(2)

enum nfp_fl_lag_batch {
 NFP_FL_LAG_BATCH_FIRST,
 NFP_FL_LAG_BATCH_MEMBER,
 NFP_FL_LAG_BATCH_FINISHED
};

/**
 * struct nfp_flower_cmsg_lag_config - control message payload for LAG config
 * @ctrl_flags: Configuration flags
 * @reserved: Reserved for future use
 * @ttl: Time to live of packet - host always sets to 0xff
 * @pkt_number: Config message packet number - increment for each message
 * @batch_ver: Batch version of messages - increment for each batch of messages
 * @group_id: Group ID applicable
 * @group_inst: Group instance number - increment when group is reused
 * @members: Array of 32-bit words listing all active group members
 */

struct nfp_flower_cmsg_lag_config {
 u8 ctrl_flags;
 u8 reserved[2];
 u8 ttl;
 __be32 pkt_number;
 __be32 batch_ver;
 __be32 group_id;
 __be32 group_inst;
 __be32 members[];
};

/**
 * struct nfp_fl_lag_group - list entry for each LAG group
 * @group_id: Assigned group ID for host/kernel sync
 * @group_inst: Group instance in case of ID reuse
 * @list: List entry
 * @master_ndev: Group master Netdev
 * @dirty: Marked if the group needs synced to HW
 * @offloaded: Marked if the group is currently offloaded to NIC
 * @to_remove: Marked if the group should be removed from NIC
 * @to_destroy: Marked if the group should be removed from driver
 * @slave_cnt: Number of slaves in group
 */

struct nfp_fl_lag_group {
 unsigned int group_id;
 u8 group_inst;
 struct list_head list;
 struct net_device *master_ndev;
 bool dirty;
 bool offloaded;
 bool to_remove;
 bool to_destroy;
 unsigned int slave_cnt;
};

#define NFP_FL_LAG_PKT_NUMBER_MASK GENMASK(30, 0)
#define NFP_FL_LAG_VERSION_MASK  GENMASK(22, 0)
#define NFP_FL_LAG_HOST_TTL  0xff

/* Use this ID with zero members to ack a batch config */
#define NFP_FL_LAG_SYNC_ID  0
#define NFP_FL_LAG_GROUP_MIN  1 /* ID 0 reserved */
#define NFP_FL_LAG_GROUP_MAX  31 /* IDs 1 to 31 are valid */

/* wait for more config */
#define NFP_FL_LAG_DELAY  (msecs_to_jiffies(2))

#define NFP_FL_LAG_RETRANS_LIMIT 100 /* max retrans cmsgs to store */

static unsigned int nfp_fl_get_next_pkt_number(struct nfp_fl_lag *lag)
{
 lag->pkt_num++;
 lag->pkt_num &= NFP_FL_LAG_PKT_NUMBER_MASK;

 return lag->pkt_num;
}

static void nfp_fl_increment_version(struct nfp_fl_lag *lag)
{
 /* LSB is not considered by firmware so add 2 for each increment. */
 lag->batch_ver += 2;
 lag->batch_ver &= NFP_FL_LAG_VERSION_MASK;

 /* Zero is reserved by firmware. */
 if (!lag->batch_ver)
  lag->batch_ver += 2;
}

static struct nfp_fl_lag_group *
nfp_fl_lag_group_create(struct nfp_fl_lag *lag, struct net_device *master)
{
 struct nfp_fl_lag_group *group;
 struct nfp_flower_priv *priv;
 int id;

 priv = container_of(lag, struct nfp_flower_priv, nfp_lag);

 id = ida_alloc_range(&lag->ida_handle, NFP_FL_LAG_GROUP_MIN,
        NFP_FL_LAG_GROUP_MAX, GFP_KERNEL);
 if (id < 0) {
  nfp_flower_cmsg_warn(priv->app,
         "No more bonding groups available\n");
  return ERR_PTR(id);
 }

 group = kmalloc(sizeof(*group), GFP_KERNEL);
 if (!group) {
  ida_free(&lag->ida_handle, id);
  return ERR_PTR(-ENOMEM);
 }

 group->group_id = id;
 group->master_ndev = master;
 group->dirty = true;
 group->offloaded = false;
 group->to_remove = false;
 group->to_destroy = false;
 group->slave_cnt = 0;
 group->group_inst = ++lag->global_inst;
 list_add_tail(&group->list, &lag->group_list);

 return group;
}

static struct nfp_fl_lag_group *
nfp_fl_lag_find_group_for_master_with_lag(struct nfp_fl_lag *lag,
       struct net_device *master)
{
 struct nfp_fl_lag_group *entry;

 if (!master)
  return NULL;

 list_for_each_entry(entry, &lag->group_list, list)
  if (entry->master_ndev == master)
   return entry;

 return NULL;
}

static int nfp_fl_lag_get_group_info(struct nfp_app *app,
         struct net_device *netdev,
         __be16 *group_id,
         u8 *batch_ver,
         u8 *group_inst)
{
 struct nfp_flower_priv *priv = app->priv;
 struct nfp_fl_lag_group *group = NULL;
 __be32 temp_vers;

 mutex_lock(&priv->nfp_lag.lock);
 group = nfp_fl_lag_find_group_for_master_with_lag(&priv->nfp_lag,
         netdev);
 if (!group) {
  mutex_unlock(&priv->nfp_lag.lock);
  return -ENOENT;
 }

 if (group_id)
  *group_id = cpu_to_be16(group->group_id);

 if (batch_ver) {
  temp_vers = cpu_to_be32(priv->nfp_lag.batch_ver <<
     NFP_FL_PRE_LAG_VER_OFF);
  memcpy(batch_ver, &temp_vers, 3);
 }

 if (group_inst)
  *group_inst = group->group_inst;

 mutex_unlock(&priv->nfp_lag.lock);

 return 0;
}

int nfp_flower_lag_populate_pre_action(struct nfp_app *app,
           struct net_device *master,
           struct nfp_fl_pre_lag *pre_act,
           struct netlink_ext_ack *extack)
{
 if (nfp_fl_lag_get_group_info(app, master, &pre_act->group_id,
          pre_act->lag_version,
          &pre_act->instance)) {
  NL_SET_ERR_MSG_MOD(extack, "invalid entry: group does not exist for LAG action");
  return -ENOENT;
 }

 return 0;
}

void nfp_flower_lag_get_info_from_netdev(struct nfp_app *app,
      struct net_device *netdev,
      struct nfp_tun_neigh_lag *lag)
{
 nfp_fl_lag_get_group_info(app, netdev, NULL,
      lag->lag_version, &lag->lag_instance);
}

int nfp_flower_lag_get_output_id(struct nfp_app *app, struct net_device *master)
{
 struct nfp_flower_priv *priv = app->priv;
 struct nfp_fl_lag_group *group = NULL;
 int group_id = -ENOENT;

 mutex_lock(&priv->nfp_lag.lock);
 group = nfp_fl_lag_find_group_for_master_with_lag(&priv->nfp_lag,
         master);
 if (group)
  group_id = group->group_id;
 mutex_unlock(&priv->nfp_lag.lock);

 return group_id;
}

static int
nfp_fl_lag_config_group(struct nfp_fl_lag *lag, struct nfp_fl_lag_group *group,
   struct net_device **active_members,
   unsigned int member_cnt, enum nfp_fl_lag_batch *batch)
{
 struct nfp_flower_cmsg_lag_config *cmsg_payload;
 struct nfp_flower_priv *priv;
 unsigned long int flags;
 unsigned int size, i;
 struct sk_buff *skb;

 priv = container_of(lag, struct nfp_flower_priv, nfp_lag);
 size = sizeof(*cmsg_payload) + sizeof(__be32) * member_cnt;
 skb = nfp_flower_cmsg_alloc(priv->app, size,
        NFP_FLOWER_CMSG_TYPE_LAG_CONFIG,
        GFP_KERNEL);
 if (!skb)
  return -ENOMEM;

 cmsg_payload = nfp_flower_cmsg_get_data(skb);
 flags = 0;

 /* Increment batch version for each new batch of config messages. */
 if (*batch == NFP_FL_LAG_BATCH_FIRST) {
  flags |= NFP_FL_LAG_FIRST;
  nfp_fl_increment_version(lag);
  *batch = NFP_FL_LAG_BATCH_MEMBER;
 }

 /* If it is a reset msg then it is also the end of the batch. */
 if (lag->rst_cfg) {
  flags |= NFP_FL_LAG_RESET;
  *batch = NFP_FL_LAG_BATCH_FINISHED;
 }

 /* To signal the end of a batch, both the switch and last flags are set
 * and the reserved SYNC group ID is used.
 */

 if (*batch == NFP_FL_LAG_BATCH_FINISHED) {
  flags |= NFP_FL_LAG_SWITCH | NFP_FL_LAG_LAST;
  lag->rst_cfg = false;
  cmsg_payload->group_id = cpu_to_be32(NFP_FL_LAG_SYNC_ID);
  cmsg_payload->group_inst = 0;
 } else {
  cmsg_payload->group_id = cpu_to_be32(group->group_id);
  cmsg_payload->group_inst = cpu_to_be32(group->group_inst);
 }

 cmsg_payload->reserved[0] = 0;
 cmsg_payload->reserved[1] = 0;
 cmsg_payload->ttl = NFP_FL_LAG_HOST_TTL;
 cmsg_payload->ctrl_flags = flags;
 cmsg_payload->batch_ver = cpu_to_be32(lag->batch_ver);
 cmsg_payload->pkt_number = cpu_to_be32(nfp_fl_get_next_pkt_number(lag));

 for (i = 0; i < member_cnt; i++)
  cmsg_payload->members[i] =
   cpu_to_be32(nfp_repr_get_port_id(active_members[i]));

 nfp_ctrl_tx(priv->app->ctrl, skb);
 return 0;
}

static void nfp_fl_lag_do_work(struct work_struct *work)
{
 enum nfp_fl_lag_batch batch = NFP_FL_LAG_BATCH_FIRST;
 struct nfp_fl_lag_group *entry, *storage;
 struct delayed_work *delayed_work;
 struct nfp_flower_priv *priv;
 struct nfp_fl_lag *lag;
 int err;

 delayed_work = to_delayed_work(work);
 lag = container_of(delayed_work, struct nfp_fl_lag, work);
 priv = container_of(lag, struct nfp_flower_priv, nfp_lag);

 mutex_lock(&lag->lock);
 list_for_each_entry_safe(entry, storage, &lag->group_list, list) {
  struct net_device *iter_netdev, **acti_netdevs;
  struct nfp_flower_repr_priv *repr_priv;
  int active_count = 0, slaves = 0;
  struct nfp_repr *repr;
  unsigned long *flags;

  if (entry->to_remove) {
   /* Active count of 0 deletes group on hw. */
   err = nfp_fl_lag_config_group(lag, entry, NULL, 0,
            &batch);
   if (!err) {
    entry->to_remove = false;
    entry->offloaded = false;
   } else {
    nfp_flower_cmsg_warn(priv->app,
           "group delete failed\n");
    schedule_delayed_work(&lag->work,
            NFP_FL_LAG_DELAY);
    continue;
   }

   if (entry->to_destroy) {
    ida_free(&lag->ida_handle, entry->group_id);
    list_del(&entry->list);
    kfree(entry);
   }
   continue;
  }

  acti_netdevs = kmalloc_array(entry->slave_cnt,
          sizeof(*acti_netdevs), GFP_KERNEL);
  if (!acti_netdevs) {
   schedule_delayed_work(&lag->work,
           NFP_FL_LAG_DELAY);
   continue;
  }

  /* Include sanity check in the loop. It may be that a bond has
 * changed between processing the last notification and the
 * work queue triggering. If the number of slaves has changed
 * or it now contains netdevs that cannot be offloaded, ignore
 * the group until pending notifications are processed.
 */

  rcu_read_lock();
  for_each_netdev_in_bond_rcu(entry->master_ndev, iter_netdev) {
   if (!nfp_netdev_is_nfp_repr(iter_netdev)) {
    slaves = 0;
    break;
   }

   repr = netdev_priv(iter_netdev);

   if (repr->app != priv->app) {
    slaves = 0;
    break;
   }

   slaves++;
   if (slaves > entry->slave_cnt)
    break;

   /* Check the ports for state changes. */
   repr_priv = repr->app_priv;
   flags = &repr_priv->lag_port_flags;

   if (*flags & NFP_PORT_LAG_CHANGED) {
    *flags &= ~NFP_PORT_LAG_CHANGED;
    entry->dirty = true;
   }

   if ((*flags & NFP_PORT_LAG_TX_ENABLED) &&
       (*flags & NFP_PORT_LAG_LINK_UP))
    acti_netdevs[active_count++] = iter_netdev;
  }
  rcu_read_unlock();

  if (slaves != entry->slave_cnt || !entry->dirty) {
   kfree(acti_netdevs);
   continue;
  }

  err = nfp_fl_lag_config_group(lag, entry, acti_netdevs,
           active_count, &batch);
  if (!err) {
   entry->offloaded = true;
   entry->dirty = false;
  } else {
   nfp_flower_cmsg_warn(priv->app,
          "group offload failed\n");
   schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
  }

  kfree(acti_netdevs);
 }

 /* End the config batch if at least one packet has been batched. */
 if (batch == NFP_FL_LAG_BATCH_MEMBER) {
  batch = NFP_FL_LAG_BATCH_FINISHED;
  err = nfp_fl_lag_config_group(lag, NULL, NULL, 0, &batch);
  if (err)
   nfp_flower_cmsg_warn(priv->app,
          "group batch end cmsg failed\n");
 }

 mutex_unlock(&lag->lock);
}

static int
nfp_fl_lag_put_unprocessed(struct nfp_fl_lag *lag, struct sk_buff *skb)
{
 struct nfp_flower_cmsg_lag_config *cmsg_payload;

 cmsg_payload = nfp_flower_cmsg_get_data(skb);
 if (be32_to_cpu(cmsg_payload->group_id) > NFP_FL_LAG_GROUP_MAX)
  return -EINVAL;

 /* Drop cmsg retrans if storage limit is exceeded to prevent
 * overloading. If the fw notices that expected messages have not been
 * received in a given time block, it will request a full resync.
 */

 if (skb_queue_len(&lag->retrans_skbs) >= NFP_FL_LAG_RETRANS_LIMIT)
  return -ENOSPC;

 __skb_queue_tail(&lag->retrans_skbs, skb);

 return 0;
}

static void nfp_fl_send_unprocessed(struct nfp_fl_lag *lag)
{
 struct nfp_flower_priv *priv;
 struct sk_buff *skb;

 priv = container_of(lag, struct nfp_flower_priv, nfp_lag);

 while ((skb = __skb_dequeue(&lag->retrans_skbs)))
  nfp_ctrl_tx(priv->app->ctrl, skb);
}

bool nfp_flower_lag_unprocessed_msg(struct nfp_app *app, struct sk_buff *skb)
{
 struct nfp_flower_cmsg_lag_config *cmsg_payload;
 struct nfp_flower_priv *priv = app->priv;
 struct nfp_fl_lag_group *group_entry;
 unsigned long int flags;
 bool store_skb = false;
 int err;

 cmsg_payload = nfp_flower_cmsg_get_data(skb);
 flags = cmsg_payload->ctrl_flags;

 /* Note the intentional fall through below. If DATA and XON are both
 * set, the message will stored and sent again with the rest of the
 * unprocessed messages list.
 */


 /* Store */
 if (flags & NFP_FL_LAG_DATA)
  if (!nfp_fl_lag_put_unprocessed(&priv->nfp_lag, skb))
   store_skb = true;

 /* Send stored */
 if (flags & NFP_FL_LAG_XON)
  nfp_fl_send_unprocessed(&priv->nfp_lag);

 /* Resend all */
 if (flags & NFP_FL_LAG_SYNC) {
  /* To resend all config:
 * 1) Clear all unprocessed messages
 * 2) Mark all groups dirty
 * 3) Reset NFP group config
 * 4) Schedule a LAG config update
 */


  __skb_queue_purge(&priv->nfp_lag.retrans_skbs);

  mutex_lock(&priv->nfp_lag.lock);
  list_for_each_entry(group_entry, &priv->nfp_lag.group_list,
        list)
   group_entry->dirty = true;

  err = nfp_flower_lag_reset(&priv->nfp_lag);
  if (err)
   nfp_flower_cmsg_warn(priv->app,
          "mem err in group reset msg\n");
  mutex_unlock(&priv->nfp_lag.lock);

  schedule_delayed_work(&priv->nfp_lag.work, 0);
 }

 return store_skb;
}

static void
nfp_fl_lag_schedule_group_remove(struct nfp_fl_lag *lag,
     struct nfp_fl_lag_group *group)
{
 group->to_remove = true;

 schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
}

static void
nfp_fl_lag_schedule_group_delete(struct nfp_fl_lag *lag,
     struct net_device *master)
{
 struct nfp_fl_lag_group *group;
 struct nfp_flower_priv *priv;

 priv = container_of(lag, struct nfp_flower_priv, nfp_lag);

 if (!netif_is_bond_master(master))
  return;

 mutex_lock(&lag->lock);
 group = nfp_fl_lag_find_group_for_master_with_lag(lag, master);
 if (!group) {
  mutex_unlock(&lag->lock);
  nfp_warn(priv->app->cpp, "untracked bond got unregistered %s\n",
    netdev_name(master));
  return;
 }

 group->to_remove = true;
 group->to_destroy = true;
 mutex_unlock(&lag->lock);

 schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
}

static int
nfp_fl_lag_changeupper_event(struct nfp_fl_lag *lag,
        struct netdev_notifier_changeupper_info *info)
{
 struct net_device *upper = info->upper_dev, *iter_netdev;
 struct netdev_lag_upper_info *lag_upper_info;
 struct nfp_fl_lag_group *group;
 struct nfp_flower_priv *priv;
 unsigned int slave_count = 0;
 bool can_offload = true;
 struct nfp_repr *repr;

 if (!netif_is_lag_master(upper))
  return 0;

 priv = container_of(lag, struct nfp_flower_priv, nfp_lag);

 rcu_read_lock();
 for_each_netdev_in_bond_rcu(upper, iter_netdev) {
  if (!nfp_netdev_is_nfp_repr(iter_netdev)) {
   can_offload = false;
   break;
  }
  repr = netdev_priv(iter_netdev);

  /* Ensure all ports are created by the same app/on same card. */
  if (repr->app != priv->app) {
   can_offload = false;
   break;
  }

  slave_count++;
 }
 rcu_read_unlock();

 lag_upper_info = info->upper_info;

 /* Firmware supports active/backup and L3/L4 hash bonds. */
 if (lag_upper_info &&
     lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_ACTIVEBACKUP &&
     (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH ||
      (lag_upper_info->hash_type != NETDEV_LAG_HASH_L34 &&
       lag_upper_info->hash_type != NETDEV_LAG_HASH_E34 &&
       lag_upper_info->hash_type != NETDEV_LAG_HASH_UNKNOWN))) {
  can_offload = false;
  nfp_flower_cmsg_warn(priv->app,
         "Unable to offload tx_type %u hash %u\n",
         lag_upper_info->tx_type,
         lag_upper_info->hash_type);
 }

 mutex_lock(&lag->lock);
 group = nfp_fl_lag_find_group_for_master_with_lag(lag, upper);

 if (slave_count == 0 || !can_offload) {
  /* Cannot offload the group - remove if previously offloaded. */
  if (group && group->offloaded)
   nfp_fl_lag_schedule_group_remove(lag, group);

  mutex_unlock(&lag->lock);
  return 0;
 }

 if (!group) {
  group = nfp_fl_lag_group_create(lag, upper);
  if (IS_ERR(group)) {
   mutex_unlock(&lag->lock);
   return PTR_ERR(group);
  }
 }

 group->dirty = true;
 group->slave_cnt = slave_count;

 /* Group may have been on queue for removal but is now offloadable. */
 group->to_remove = false;
 mutex_unlock(&lag->lock);

 schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
 return 0;
}

static void
nfp_fl_lag_changels_event(struct nfp_fl_lag *lag, struct net_device *netdev,
     struct netdev_notifier_changelowerstate_info *info)
{
 struct netdev_lag_lower_state_info *lag_lower_info;
 struct nfp_flower_repr_priv *repr_priv;
 struct nfp_flower_priv *priv;
 struct nfp_repr *repr;
 unsigned long *flags;

 if (!netif_is_lag_port(netdev) || !nfp_netdev_is_nfp_repr(netdev))
  return;

 lag_lower_info = info->lower_state_info;
 if (!lag_lower_info)
  return;

 priv = container_of(lag, struct nfp_flower_priv, nfp_lag);
 repr = netdev_priv(netdev);

 /* Verify that the repr is associated with this app. */
 if (repr->app != priv->app)
  return;

 repr_priv = repr->app_priv;
 flags = &repr_priv->lag_port_flags;

 mutex_lock(&lag->lock);
 if (lag_lower_info->link_up)
  *flags |= NFP_PORT_LAG_LINK_UP;
 else
  *flags &= ~NFP_PORT_LAG_LINK_UP;

 if (lag_lower_info->tx_enabled)
  *flags |= NFP_PORT_LAG_TX_ENABLED;
 else
  *flags &= ~NFP_PORT_LAG_TX_ENABLED;

 *flags |= NFP_PORT_LAG_CHANGED;
 mutex_unlock(&lag->lock);

 schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
}

int nfp_flower_lag_netdev_event(struct nfp_flower_priv *priv,
    struct net_device *netdev,
    unsigned long event, void *ptr)
{
 struct nfp_fl_lag *lag = &priv->nfp_lag;
 int err;

 switch (event) {
 case NETDEV_CHANGEUPPER:
  err = nfp_fl_lag_changeupper_event(lag, ptr);
  if (err)
   return NOTIFY_BAD;
  return NOTIFY_OK;
 case NETDEV_CHANGELOWERSTATE:
  nfp_fl_lag_changels_event(lag, netdev, ptr);
  return NOTIFY_OK;
 case NETDEV_UNREGISTER:
  nfp_fl_lag_schedule_group_delete(lag, netdev);
  return NOTIFY_OK;
 }

 return NOTIFY_DONE;
}

int nfp_flower_lag_reset(struct nfp_fl_lag *lag)
{
 enum nfp_fl_lag_batch batch = NFP_FL_LAG_BATCH_FIRST;

 lag->rst_cfg = true;
 return nfp_fl_lag_config_group(lag, NULL, NULL, 0, &batch);
}

void nfp_flower_lag_init(struct nfp_fl_lag *lag)
{
 INIT_DELAYED_WORK(&lag->work, nfp_fl_lag_do_work);
 INIT_LIST_HEAD(&lag->group_list);
 mutex_init(&lag->lock);
 ida_init(&lag->ida_handle);

 __skb_queue_head_init(&lag->retrans_skbs);

 /* 0 is a reserved batch version so increment to first valid value. */
 nfp_fl_increment_version(lag);
}

void nfp_flower_lag_cleanup(struct nfp_fl_lag *lag)
{
 struct nfp_fl_lag_group *entry, *storage;

 cancel_delayed_work_sync(&lag->work);

 __skb_queue_purge(&lag->retrans_skbs);

 /* Remove all groups. */
 mutex_lock(&lag->lock);
 list_for_each_entry_safe(entry, storage, &lag->group_list, list) {
  list_del(&entry->list);
  kfree(entry);
 }
 mutex_unlock(&lag->lock);
 mutex_destroy(&lag->lock);
 ida_destroy(&lag->ida_handle);
}

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

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