Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  rss.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
// Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES.

#include "rss.h"

#define mlx5e_rss_warn(__dev, format, ...)   \
 dev_warn((__dev)->device, "%s:%d:(pid %d): " format, \
   __func__, __LINE__, current->pid,  \
   ##__VA_ARGS__)

static const struct mlx5e_rss_params_traffic_type rss_default_config[MLX5E_NUM_INDIR_TIRS] = {
 [MLX5_TT_IPV4_TCP] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
  .l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
  .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
 },
 [MLX5_TT_IPV6_TCP] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
  .l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
  .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
 },
 [MLX5_TT_IPV4_UDP] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
  .l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
  .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
 },
 [MLX5_TT_IPV6_UDP] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
  .l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
  .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
 },
 [MLX5_TT_IPV4_IPSEC_AH] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
  .l4_prot_type = 0,
  .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
 },
 [MLX5_TT_IPV6_IPSEC_AH] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
  .l4_prot_type = 0,
  .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
 },
 [MLX5_TT_IPV4_IPSEC_ESP] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
  .l4_prot_type = 0,
  .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
 },
 [MLX5_TT_IPV6_IPSEC_ESP] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
  .l4_prot_type = 0,
  .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
 },
 [MLX5_TT_IPV4] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
  .l4_prot_type = 0,
  .rx_hash_fields = MLX5_HASH_IP,
 },
 [MLX5_TT_IPV6] = {
  .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
  .l4_prot_type = 0,
  .rx_hash_fields = MLX5_HASH_IP,
 },
};

struct mlx5e_rss_params_traffic_type
mlx5e_rss_get_default_tt_config(enum mlx5_traffic_types tt)
{
 return rss_default_config[tt];
}

struct mlx5e_rss {
 struct mlx5e_rss_params_hash hash;
 struct mlx5e_rss_params_indir indir;
 u32 rx_hash_fields[MLX5E_NUM_INDIR_TIRS];
 struct mlx5e_tir *tir[MLX5E_NUM_INDIR_TIRS];
 struct mlx5e_tir *inner_tir[MLX5E_NUM_INDIR_TIRS];
 struct mlx5e_rqt rqt;
 struct mlx5_core_dev *mdev; /* primary */
 u32 drop_rqn;
 bool inner_ft_support;
 bool enabled;
 refcount_t refcnt;
};

bool mlx5e_rss_get_inner_ft_support(struct mlx5e_rss *rss)
{
 return rss->inner_ft_support;
}

void mlx5e_rss_params_indir_modify_actual_size(struct mlx5e_rss *rss, u32 num_channels)
{
 rss->indir.actual_table_size = mlx5e_rqt_size(rss->mdev, num_channels);
}

int mlx5e_rss_params_indir_init(struct mlx5e_rss_params_indir *indir, struct mlx5_core_dev *mdev,
    u32 actual_table_size, u32 max_table_size)
{
 indir->table = kvmalloc_array(max_table_size, sizeof(*indir->table), GFP_KERNEL);
 if (!indir->table)
  return -ENOMEM;

 indir->max_table_size = max_table_size;
 indir->actual_table_size = actual_table_size;

 return 0;
}

void mlx5e_rss_params_indir_cleanup(struct mlx5e_rss_params_indir *indir)
{
 kvfree(indir->table);
}

static int mlx5e_rss_copy(struct mlx5e_rss *to, const struct mlx5e_rss *from)
{
 u32 *dst_indir_table;

 if (to->indir.actual_table_size != from->indir.actual_table_size ||
     to->indir.max_table_size != from->indir.max_table_size) {
  mlx5e_rss_warn(to->mdev,
          "Failed to copy RSS due to size mismatch, src (actual %u, max %u) != dst (actual %u, max %u)\n",
          from->indir.actual_table_size, from->indir.max_table_size,
          to->indir.actual_table_size, to->indir.max_table_size);
  return -EINVAL;
 }

 dst_indir_table = to->indir.table;
 *to = *from;
 to->indir.table = dst_indir_table;
 memcpy(to->indir.table, from->indir.table,
        from->indir.actual_table_size * sizeof(*from->indir.table));
 return 0;
}

static struct mlx5e_rss *mlx5e_rss_init_copy(const struct mlx5e_rss *from)
{
 struct mlx5e_rss *rss;
 int err;

 rss = kvzalloc(sizeof(*rss), GFP_KERNEL);
 if (!rss)
  return ERR_PTR(-ENOMEM);

 err = mlx5e_rss_params_indir_init(&rss->indir, from->mdev, from->indir.actual_table_size,
       from->indir.max_table_size);
 if (err)
  goto err_free_rss;

 err = mlx5e_rss_copy(rss, from);
 if (err)
  goto err_free_indir;

 return rss;

err_free_indir:
 mlx5e_rss_params_indir_cleanup(&rss->indir);
err_free_rss:
 kvfree(rss);
 return ERR_PTR(err);
}

static void mlx5e_rss_params_init(struct mlx5e_rss *rss)
{
 enum mlx5_traffic_types tt;

 rss->hash.symmetric = true;
 rss->hash.hfunc = ETH_RSS_HASH_TOP;
 netdev_rss_key_fill(rss->hash.toeplitz_hash_key,
       sizeof(rss->hash.toeplitz_hash_key));
 for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
  rss->rx_hash_fields[tt] =
   mlx5e_rss_get_default_tt_config(tt).rx_hash_fields;
}

static struct mlx5e_tir **rss_get_tirp(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
           bool inner)
{
 return inner ? &rss->inner_tir[tt] : &rss->tir[tt];
}

static struct mlx5e_tir *rss_get_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
         bool inner)
{
 return *rss_get_tirp(rss, tt, inner);
}

static struct mlx5e_rss_params_traffic_type
mlx5e_rss_get_tt_config(struct mlx5e_rss *rss, enum mlx5_traffic_types tt)
{
 struct mlx5e_rss_params_traffic_type rss_tt;

 rss_tt = mlx5e_rss_get_default_tt_config(tt);
 rss_tt.rx_hash_fields = rss->rx_hash_fields[tt];
 return rss_tt;
}

static int mlx5e_rss_create_tir(struct mlx5e_rss *rss,
    enum mlx5_traffic_types tt,
    const struct mlx5e_packet_merge_param *init_pkt_merge_param,
    bool inner)
{
 struct mlx5e_rss_params_traffic_type rss_tt;
 struct mlx5e_tir_builder *builder;
 struct mlx5e_tir **tir_p;
 struct mlx5e_tir *tir;
 u32 rqtn;
 int err;

 if (inner && !rss->inner_ft_support) {
  mlx5e_rss_warn(rss->mdev,
          "Cannot create inner indirect TIR[%d], RSS inner FT is not supported.\n",
          tt);
  return -EINVAL;
 }

 tir_p = rss_get_tirp(rss, tt, inner);
 if (*tir_p)
  return -EINVAL;

 tir = kvzalloc(sizeof(*tir), GFP_KERNEL);
 if (!tir)
  return -ENOMEM;

 builder = mlx5e_tir_builder_alloc(false);
 if (!builder) {
  err = -ENOMEM;
  goto free_tir;
 }

 rqtn = mlx5e_rqt_get_rqtn(&rss->rqt);
 mlx5e_tir_builder_build_rqt(builder, rss->mdev->mlx5e_res.hw_objs.td.tdn,
        rqtn, rss->inner_ft_support);
 mlx5e_tir_builder_build_packet_merge(builder, init_pkt_merge_param);
 rss_tt = mlx5e_rss_get_tt_config(rss, tt);
 mlx5e_tir_builder_build_rss(builder, &rss->hash, &rss_tt, inner);

 err = mlx5e_tir_init(tir, builder, rss->mdev, true);
 mlx5e_tir_builder_free(builder);
 if (err) {
  mlx5e_rss_warn(rss->mdev, "Failed to create %sindirect TIR: err = %d, tt = %d\n",
          inner ? "inner " : "", err, tt);
  goto free_tir;
 }

 *tir_p = tir;
 return 0;

free_tir:
 kvfree(tir);
 return err;
}

static void mlx5e_rss_destroy_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
      bool inner)
{
 struct mlx5e_tir **tir_p;
 struct mlx5e_tir *tir;

 tir_p = rss_get_tirp(rss, tt, inner);
 if (!*tir_p)
  return;

 tir = *tir_p;
 mlx5e_tir_destroy(tir);
 kvfree(tir);
 *tir_p = NULL;
}

static int mlx5e_rss_create_tirs(struct mlx5e_rss *rss,
     const struct mlx5e_packet_merge_param *init_pkt_merge_param,
     bool inner)
{
 enum mlx5_traffic_types tt, max_tt;
 int err;

 for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
  err = mlx5e_rss_create_tir(rss, tt, init_pkt_merge_param, inner);
  if (err)
   goto err_destroy_tirs;
 }

 return 0;

err_destroy_tirs:
 max_tt = tt;
 for (tt = 0; tt < max_tt; tt++)
  mlx5e_rss_destroy_tir(rss, tt, inner);
 return err;
}

static void mlx5e_rss_destroy_tirs(struct mlx5e_rss *rss, bool inner)
{
 enum mlx5_traffic_types tt;

 for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
  mlx5e_rss_destroy_tir(rss, tt, inner);
}

static int mlx5e_rss_update_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
    bool inner)
{
 struct mlx5e_rss_params_traffic_type rss_tt;
 struct mlx5e_tir_builder *builder;
 struct mlx5e_tir *tir;
 int err;

 tir = rss_get_tir(rss, tt, inner);
 if (!tir)
  return 0;

 builder = mlx5e_tir_builder_alloc(true);
 if (!builder)
  return -ENOMEM;

 rss_tt = mlx5e_rss_get_tt_config(rss, tt);

 mlx5e_tir_builder_build_rss(builder, &rss->hash, &rss_tt, inner);
 err = mlx5e_tir_modify(tir, builder);

 mlx5e_tir_builder_free(builder);
 return err;
}

static int mlx5e_rss_update_tirs(struct mlx5e_rss *rss)
{
 enum mlx5_traffic_types tt;
 int err, retval;

 retval = 0;

 for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
  err = mlx5e_rss_update_tir(rss, tt, false);
  if (err) {
   retval = retval ? : err;
   mlx5e_rss_warn(rss->mdev,
           "Failed to update RSS hash of indirect TIR for traffic type %d: err = %d\n",
           tt, err);
  }

  if (!rss->inner_ft_support)
   continue;

  err = mlx5e_rss_update_tir(rss, tt, true);
  if (err) {
   retval = retval ? : err;
   mlx5e_rss_warn(rss->mdev,
           "Failed to update RSS hash of inner indirect TIR for traffic type %d: err = %d\n",
           tt, err);
  }
 }
 return retval;
}

static int mlx5e_rss_init_no_tirs(struct mlx5e_rss *rss)
{
 mlx5e_rss_params_init(rss);
 refcount_set(&rss->refcnt, 1);

 return mlx5e_rqt_init_direct(&rss->rqt, rss->mdev, true,
         rss->drop_rqn, rss->indir.max_table_size);
}

struct mlx5e_rss *mlx5e_rss_init(struct mlx5_core_dev *mdev, bool inner_ft_support, u32 drop_rqn,
     const struct mlx5e_packet_merge_param *init_pkt_merge_param,
     enum mlx5e_rss_init_type type, unsigned int nch,
     unsigned int max_nch)
{
 struct mlx5e_rss *rss;
 int err;

 rss = kvzalloc(sizeof(*rss), GFP_KERNEL);
 if (!rss)
  return ERR_PTR(-ENOMEM);

 err = mlx5e_rss_params_indir_init(&rss->indir, mdev,
       mlx5e_rqt_size(mdev, nch),
       mlx5e_rqt_size(mdev, max_nch));
 if (err)
  goto err_free_rss;

 rss->mdev = mdev;
 rss->inner_ft_support = inner_ft_support;
 rss->drop_rqn = drop_rqn;

 err = mlx5e_rss_init_no_tirs(rss);
 if (err)
  goto err_free_indir;

 if (type == MLX5E_RSS_INIT_NO_TIRS)
  goto out;

 err = mlx5e_rss_create_tirs(rss, init_pkt_merge_param, false);
 if (err)
  goto err_destroy_rqt;

 if (inner_ft_support) {
  err = mlx5e_rss_create_tirs(rss, init_pkt_merge_param, true);
  if (err)
   goto err_destroy_tirs;
 }

out:
 return rss;

err_destroy_tirs:
 mlx5e_rss_destroy_tirs(rss, false);
err_destroy_rqt:
 mlx5e_rqt_destroy(&rss->rqt);
err_free_indir:
 mlx5e_rss_params_indir_cleanup(&rss->indir);
err_free_rss:
 kvfree(rss);
 return ERR_PTR(err);
}

int mlx5e_rss_cleanup(struct mlx5e_rss *rss)
{
 if (!refcount_dec_if_one(&rss->refcnt))
  return -EBUSY;

 mlx5e_rss_destroy_tirs(rss, false);

 if (rss->inner_ft_support)
  mlx5e_rss_destroy_tirs(rss, true);

 mlx5e_rqt_destroy(&rss->rqt);
 mlx5e_rss_params_indir_cleanup(&rss->indir);
 kvfree(rss);

 return 0;
}

void mlx5e_rss_refcnt_inc(struct mlx5e_rss *rss)
{
 refcount_inc(&rss->refcnt);
}

void mlx5e_rss_refcnt_dec(struct mlx5e_rss *rss)
{
 refcount_dec(&rss->refcnt);
}

unsigned int mlx5e_rss_refcnt_read(struct mlx5e_rss *rss)
{
 return refcount_read(&rss->refcnt);
}

u32 mlx5e_rss_get_tirn(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
         bool inner)
{
 struct mlx5e_tir *tir;

 WARN_ON(inner && !rss->inner_ft_support);
 tir = rss_get_tir(rss, tt, inner);
 WARN_ON(!tir);

 return mlx5e_tir_get_tirn(tir);
}

u32 mlx5e_rss_get_rqtn(struct mlx5e_rss *rss)
{
 return mlx5e_rqt_get_rqtn(&rss->rqt);
}

bool mlx5e_rss_valid_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt, bool inner)
{
 return !!rss_get_tir(rss, tt, inner);
}

/* Fill the "tirn" output parameter.
 * Create the requested TIR if it's its first usage.
 */

int mlx5e_rss_obtain_tirn(struct mlx5e_rss *rss,
     enum mlx5_traffic_types tt,
     const struct mlx5e_packet_merge_param *init_pkt_merge_param,
     bool inner, u32 *tirn)
{
 struct mlx5e_tir *tir;

 tir = rss_get_tir(rss, tt, inner);
 if (!tir) { /* TIR doesn't exist, create one */
  int err;

  err = mlx5e_rss_create_tir(rss, tt, init_pkt_merge_param, inner);
  if (err)
   return err;
  tir = rss_get_tir(rss, tt, inner);
 }

 *tirn = mlx5e_tir_get_tirn(tir);
 return 0;
}

static int mlx5e_rss_apply(struct mlx5e_rss *rss, u32 *rqns, u32 *vhca_ids, unsigned int num_rqns)
{
 int err;

 err = mlx5e_rqt_redirect_indir(&rss->rqt, rqns, vhca_ids, num_rqns, rss->hash.hfunc,
           &rss->indir);
 if (err)
  mlx5e_rss_warn(rss->mdev, "Failed to redirect RQT %#x to channels: err = %d\n",
          mlx5e_rqt_get_rqtn(&rss->rqt), err);
 return err;
}

void mlx5e_rss_enable(struct mlx5e_rss *rss, u32 *rqns, u32 *vhca_ids, unsigned int num_rqns)
{
 rss->enabled = true;
 mlx5e_rss_apply(rss, rqns, vhca_ids, num_rqns);
}

void mlx5e_rss_disable(struct mlx5e_rss *rss)
{
 int err;

 rss->enabled = false;
 err = mlx5e_rqt_redirect_direct(&rss->rqt, rss->drop_rqn, NULL);
 if (err)
  mlx5e_rss_warn(rss->mdev, "Failed to redirect RQT %#x to drop RQ %#x: err = %d\n",
          mlx5e_rqt_get_rqtn(&rss->rqt), rss->drop_rqn, err);
}

int mlx5e_rss_packet_merge_set_param(struct mlx5e_rss *rss,
         struct mlx5e_packet_merge_param *pkt_merge_param)
{
 struct mlx5e_tir_builder *builder;
 enum mlx5_traffic_types tt;
 int err, final_err;

 builder = mlx5e_tir_builder_alloc(true);
 if (!builder)
  return -ENOMEM;

 mlx5e_tir_builder_build_packet_merge(builder, pkt_merge_param);

 final_err = 0;

 for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
  struct mlx5e_tir *tir;

  tir = rss_get_tir(rss, tt, false);
  if (!tir)
   goto inner_tir;
  err = mlx5e_tir_modify(tir, builder);
  if (err) {
   mlx5e_rss_warn(rss->mdev, "Failed to update packet merge state of indirect TIR %#x for traffic type %d: err = %d\n",
           mlx5e_tir_get_tirn(tir), tt, err);
   if (!final_err)
    final_err = err;
  }

inner_tir:
  if (!rss->inner_ft_support)
   continue;

  tir = rss_get_tir(rss, tt, true);
  if (!tir)
   continue;
  err = mlx5e_tir_modify(tir, builder);
  if (err) {
   mlx5e_rss_warn(rss->mdev, "Failed to update packet merge state of inner indirect TIR %#x for traffic type %d: err = %d\n",
           mlx5e_tir_get_tirn(tir), tt, err);
   if (!final_err)
    final_err = err;
  }
 }

 mlx5e_tir_builder_free(builder);
 return final_err;
}

void mlx5e_rss_get_rxfh(struct mlx5e_rss *rss, u32 *indir, u8 *key, u8 *hfunc,
   bool *symmetric)
{
 if (indir)
  memcpy(indir, rss->indir.table,
         rss->indir.actual_table_size * sizeof(*rss->indir.table));

 if (key)
  memcpy(key, rss->hash.toeplitz_hash_key,
         sizeof(rss->hash.toeplitz_hash_key));

 if (hfunc)
  *hfunc = rss->hash.hfunc;

 if (symmetric)
  *symmetric = rss->hash.symmetric;
}

int mlx5e_rss_set_rxfh(struct mlx5e_rss *rss, const u32 *indir,
         const u8 *key, const u8 *hfunc, const bool *symmetric,
         u32 *rqns, u32 *vhca_ids, unsigned int num_rqns)
{
 bool changed_indir = false;
 bool changed_hash = false;
 struct mlx5e_rss *old_rss;
 int err = 0;

 old_rss = mlx5e_rss_init_copy(rss);
 if (IS_ERR(old_rss))
  return PTR_ERR(old_rss);

 if (hfunc && *hfunc != rss->hash.hfunc) {
  switch (*hfunc) {
  case ETH_RSS_HASH_XOR:
  case ETH_RSS_HASH_TOP:
   break;
  default:
   err = -EINVAL;
   goto out;
  }
  changed_hash = true;
  changed_indir = true;
  rss->hash.hfunc = *hfunc;
 }

 if (key) {
  if (rss->hash.hfunc == ETH_RSS_HASH_TOP)
   changed_hash = true;
  memcpy(rss->hash.toeplitz_hash_key, key,
         sizeof(rss->hash.toeplitz_hash_key));
 }

 if (indir) {
  changed_indir = true;

  memcpy(rss->indir.table, indir,
         rss->indir.actual_table_size * sizeof(*rss->indir.table));
 }

 if (symmetric) {
  rss->hash.symmetric = *symmetric;
  changed_hash = true;
 }

 if (changed_indir && rss->enabled) {
  err = mlx5e_rss_apply(rss, rqns, vhca_ids, num_rqns);
  if (err) {
   mlx5e_rss_copy(rss, old_rss);
   goto out;
  }
 }

 if (changed_hash)
  mlx5e_rss_update_tirs(rss);

out:
 mlx5e_rss_params_indir_cleanup(&old_rss->indir);
 kvfree(old_rss);

 return err;
}

struct mlx5e_rss_params_hash mlx5e_rss_get_hash(struct mlx5e_rss *rss)
{
 return rss->hash;
}

u8 mlx5e_rss_get_hash_fields(struct mlx5e_rss *rss, enum mlx5_traffic_types tt)
{
 return rss->rx_hash_fields[tt];
}

int mlx5e_rss_set_hash_fields(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
         u8 rx_hash_fields)
{
 u8 old_rx_hash_fields;
 int err;

 old_rx_hash_fields = rss->rx_hash_fields[tt];

 if (old_rx_hash_fields == rx_hash_fields)
  return 0;

 rss->rx_hash_fields[tt] = rx_hash_fields;

 err = mlx5e_rss_update_tir(rss, tt, false);
 if (err) {
  rss->rx_hash_fields[tt] = old_rx_hash_fields;
  mlx5e_rss_warn(rss->mdev,
          "Failed to update RSS hash fields of indirect TIR for traffic type %d: err = %d\n",
          tt, err);
  return err;
 }

 if (!(rss->inner_ft_support))
  return 0;

 err = mlx5e_rss_update_tir(rss, tt, true);
 if (err) {
  /* Partial update happened. Try to revert - it may fail too, but
 * there is nothing more we can do.
 */

  rss->rx_hash_fields[tt] = old_rx_hash_fields;
  mlx5e_rss_warn(rss->mdev,
          "Failed to update RSS hash fields of inner indirect TIR for traffic type %d: err = %d\n",
          tt, err);
  if (mlx5e_rss_update_tir(rss, tt, false))
   mlx5e_rss_warn(rss->mdev,
           "Partial update of RSS hash fields happened: failed to revert indirect TIR for traffic type %d to the old values\n",
           tt);
 }

 return err;
}

void mlx5e_rss_set_indir_uniform(struct mlx5e_rss *rss, unsigned int nch)
{
 mlx5e_rss_params_indir_init_uniform(&rss->indir, nch);
}

Messung V0.5
C=99 H=91 G=94

¤ 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge