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


Quelle  lockdep.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * kernel/lockdep.c
 *
 * Runtime locking correctness validator
 *
 * Started by Ingo Molnar:
 *
 *  Copyright (C) 2006,2007 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
 *  Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
 *
 * this code maps all the lock dependencies as they occur in a live kernel
 * and will warn about the following classes of locking bugs:
 *
 * - lock inversion scenarios
 * - circular lock dependencies
 * - hardirq/softirq safe/unsafe locking bugs
 *
 * Bugs are reported even if the current locking scenario does not cause
 * any deadlock at this point.
 *
 * I.e. if anytime in the past two locks were taken in a different order,
 * even if it happened for another task, even if those were different
 * locks (but of the same class as this lock), this code will detect it.
 *
 * Thanks to Arjan van de Ven for coming up with the initial idea of
 * mapping lock dependencies runtime.
 */

#define DISABLE_BRANCH_PROFILING
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/sched/task.h>
#include <linux/sched/mm.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
#include <linux/interrupt.h>
#include <linux/stacktrace.h>
#include <linux/debug_locks.h>
#include <linux/irqflags.h>
#include <linux/utsname.h>
#include <linux/hash.h>
#include <linux/ftrace.h>
#include <linux/stringify.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/gfp.h>
#include <linux/random.h>
#include <linux/jhash.h>
#include <linux/nmi.h>
#include <linux/rcupdate.h>
#include <linux/kprobes.h>
#include <linux/lockdep.h>
#include <linux/context_tracking.h>
#include <linux/console.h>
#include <linux/kasan.h>

#include <asm/sections.h>

#include "lockdep_internals.h"
#include "lock_events.h"

#include <trace/events/lock.h>

#ifdef CONFIG_PROVE_LOCKING
static int prove_locking = 1;
module_param(prove_locking, int, 0644);
#else
#define prove_locking 0
#endif

#ifdef CONFIG_LOCK_STAT
static int lock_stat = 1;
module_param(lock_stat, int, 0644);
#else
#define lock_stat 0
#endif

#ifdef CONFIG_SYSCTL
static const struct ctl_table kern_lockdep_table[] = {
#ifdef CONFIG_PROVE_LOCKING
 {
  .procname       = "prove_locking",
  .data           = &prove_locking,
  .maxlen         = sizeof(int),
  .mode           = 0644,
  .proc_handler   = proc_dointvec,
 },
#endif /* CONFIG_PROVE_LOCKING */
#ifdef CONFIG_LOCK_STAT
 {
  .procname       = "lock_stat",
  .data           = &lock_stat,
  .maxlen         = sizeof(int),
  .mode           = 0644,
  .proc_handler   = proc_dointvec,
 },
#endif /* CONFIG_LOCK_STAT */
};

static __init int kernel_lockdep_sysctls_init(void)
{
 register_sysctl_init("kernel", kern_lockdep_table);
 return 0;
}
late_initcall(kernel_lockdep_sysctls_init);
#endif /* CONFIG_SYSCTL */

DEFINE_PER_CPU(unsigned int, lockdep_recursion);
EXPORT_PER_CPU_SYMBOL_GPL(lockdep_recursion);

static __always_inline bool lockdep_enabled(void)
{
 if (!debug_locks)
  return false;

 if (this_cpu_read(lockdep_recursion))
  return false;

 if (current->lockdep_recursion)
  return false;

 return true;
}

/*
 * lockdep_lock: protects the lockdep graph, the hashes and the
 *               class/list/hash allocators.
 *
 * This is one of the rare exceptions where it's justified
 * to use a raw spinlock - we really dont want the spinlock
 * code to recurse back into the lockdep code...
 */

static arch_spinlock_t __lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
static struct task_struct *__owner;

static inline void lockdep_lock(void)
{
 DEBUG_LOCKS_WARN_ON(!irqs_disabled());

 __this_cpu_inc(lockdep_recursion);
 arch_spin_lock(&__lock);
 __owner = current;
}

static inline void lockdep_unlock(void)
{
 DEBUG_LOCKS_WARN_ON(!irqs_disabled());

 if (debug_locks && DEBUG_LOCKS_WARN_ON(__owner != current))
  return;

 __owner = NULL;
 arch_spin_unlock(&__lock);
 __this_cpu_dec(lockdep_recursion);
}

#ifdef CONFIG_PROVE_LOCKING
static inline bool lockdep_assert_locked(void)
{
 return DEBUG_LOCKS_WARN_ON(__owner != current);
}
#endif

static struct task_struct *lockdep_selftest_task_struct;


static int graph_lock(void)
{
 lockdep_lock();
 lockevent_inc(lockdep_lock);
 /*
 * Make sure that if another CPU detected a bug while
 * walking the graph we dont change it (while the other
 * CPU is busy printing out stuff with the graph lock
 * dropped already)
 */

 if (!debug_locks) {
  lockdep_unlock();
  return 0;
 }
 return 1;
}

static inline void graph_unlock(void)
{
 lockdep_unlock();
}

/*
 * Turn lock debugging off and return with 0 if it was off already,
 * and also release the graph lock:
 */

static inline int debug_locks_off_graph_unlock(void)
{
 int ret = debug_locks_off();

 lockdep_unlock();

 return ret;
}

unsigned long nr_list_entries;
static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES];
static DECLARE_BITMAP(list_entries_in_use, MAX_LOCKDEP_ENTRIES);

/*
 * All data structures here are protected by the global debug_lock.
 *
 * nr_lock_classes is the number of elements of lock_classes[] that is
 * in use.
 */

#define KEYHASH_BITS  (MAX_LOCKDEP_KEYS_BITS - 1)
#define KEYHASH_SIZE  (1UL << KEYHASH_BITS)
static struct hlist_head lock_keys_hash[KEYHASH_SIZE];
unsigned long nr_lock_classes;
unsigned long nr_zapped_classes;
unsigned long nr_dynamic_keys;
unsigned long max_lock_class_idx;
struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
DECLARE_BITMAP(lock_classes_in_use, MAX_LOCKDEP_KEYS);

static inline struct lock_class *hlock_class(struct held_lock *hlock)
{
 unsigned int class_idx = hlock->class_idx;

 /* Don't re-read hlock->class_idx, can't use READ_ONCE() on bitfield */
 barrier();

 if (!test_bit(class_idx, lock_classes_in_use)) {
  /*
 * Someone passed in garbage, we give up.
 */

  DEBUG_LOCKS_WARN_ON(1);
  return NULL;
 }

 /*
 * At this point, if the passed hlock->class_idx is still garbage,
 * we just have to live with it
 */

 return lock_classes + class_idx;
}

#ifdef CONFIG_LOCK_STAT
static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], cpu_lock_stats);

static inline u64 lockstat_clock(void)
{
 return local_clock();
}

static int lock_point(unsigned long points[], unsigned long ip)
{
 int i;

 for (i = 0; i < LOCKSTAT_POINTS; i++) {
  if (points[i] == 0) {
   points[i] = ip;
   break;
  }
  if (points[i] == ip)
   break;
 }

 return i;
}

static void lock_time_inc(struct lock_time *lt, u64 time)
{
 if (time > lt->max)
  lt->max = time;

 if (time < lt->min || !lt->nr)
  lt->min = time;

 lt->total += time;
 lt->nr++;
}

static inline void lock_time_add(struct lock_time *src, struct lock_time *dst)
{
 if (!src->nr)
  return;

 if (src->max > dst->max)
  dst->max = src->max;

 if (src->min < dst->min || !dst->nr)
  dst->min = src->min;

 dst->total += src->total;
 dst->nr += src->nr;
}

void lock_stats(struct lock_class *classstruct lock_class_stats *stats)
{
 int cpu, i;

 memset(stats, 0, sizeof(struct lock_class_stats));
 for_each_possible_cpu(cpu) {
  struct lock_class_stats *pcs =
   &per_cpu(cpu_lock_stats, cpu)[class - lock_classes];

  for (i = 0; i < ARRAY_SIZE(stats->contention_point); i++)
   stats->contention_point[i] += pcs->contention_point[i];

  for (i = 0; i < ARRAY_SIZE(stats->contending_point); i++)
   stats->contending_point[i] += pcs->contending_point[i];

  lock_time_add(&pcs->read_waittime, &stats->read_waittime);
  lock_time_add(&pcs->write_waittime, &stats->write_waittime);

  lock_time_add(&pcs->read_holdtime, &stats->read_holdtime);
  lock_time_add(&pcs->write_holdtime, &stats->write_holdtime);

  for (i = 0; i < ARRAY_SIZE(stats->bounces); i++)
   stats->bounces[i] += pcs->bounces[i];
 }
}

void clear_lock_stats(struct lock_class *class)
{
 int cpu;

 for_each_possible_cpu(cpu) {
  struct lock_class_stats *cpu_stats =
   &per_cpu(cpu_lock_stats, cpu)[class - lock_classes];

  memset(cpu_stats, 0, sizeof(struct lock_class_stats));
 }
 memset(class->contention_point, 0, sizeof(class->contention_point));
 memset(class->contending_point, 0, sizeof(class->contending_point));
}

static struct lock_class_stats *get_lock_stats(struct lock_class *class)
{
 return &this_cpu_ptr(cpu_lock_stats)[class - lock_classes];
}

static void lock_release_holdtime(struct held_lock *hlock)
{
 struct lock_class_stats *stats;
 u64 holdtime;

 if (!lock_stat)
  return;

 holdtime = lockstat_clock() - hlock->holdtime_stamp;

 stats = get_lock_stats(hlock_class(hlock));
 if (hlock->read)
  lock_time_inc(&stats->read_holdtime, holdtime);
 else
  lock_time_inc(&stats->write_holdtime, holdtime);
}
#else
static inline void lock_release_holdtime(struct held_lock *hlock)
{
}
#endif

/*
 * We keep a global list of all lock classes. The list is only accessed with
 * the lockdep spinlock lock held. free_lock_classes is a list with free
 * elements. These elements are linked together by the lock_entry member in
 * struct lock_class.
 */

static LIST_HEAD(all_lock_classes);
static LIST_HEAD(free_lock_classes);

/**
 * struct pending_free - information about data structures about to be freed
 * @zapped: Head of a list with struct lock_class elements.
 * @lock_chains_being_freed: Bitmap that indicates which lock_chains[] elements
 * are about to be freed.
 */

struct pending_free {
 struct list_head zapped;
 DECLARE_BITMAP(lock_chains_being_freed, MAX_LOCKDEP_CHAINS);
};

/**
 * struct delayed_free - data structures used for delayed freeing
 *
 * A data structure for delayed freeing of data structures that may be
 * accessed by RCU readers at the time these were freed.
 *
 * @rcu_head:  Used to schedule an RCU callback for freeing data structures.
 * @index:     Index of @pf to which freed data structures are added.
 * @scheduled: Whether or not an RCU callback has been scheduled.
 * @pf:        Array with information about data structures about to be freed.
 */

static struct delayed_free {
 struct rcu_head  rcu_head;
 int   index;
 int   scheduled;
 struct pending_free pf[2];
} delayed_free;

/*
 * The lockdep classes are in a hash-table as well, for fast lookup:
 */

#define CLASSHASH_BITS  (MAX_LOCKDEP_KEYS_BITS - 1)
#define CLASSHASH_SIZE  (1UL << CLASSHASH_BITS)
#define __classhashfn(key) hash_long((unsigned long)key, CLASSHASH_BITS)
#define classhashentry(key) (classhash_table + __classhashfn((key)))

static struct hlist_head classhash_table[CLASSHASH_SIZE];

/*
 * We put the lock dependency chains into a hash-table as well, to cache
 * their existence:
 */

#define CHAINHASH_BITS  (MAX_LOCKDEP_CHAINS_BITS-1)
#define CHAINHASH_SIZE  (1UL << CHAINHASH_BITS)
#define __chainhashfn(chain) hash_long(chain, CHAINHASH_BITS)
#define chainhashentry(chain) (chainhash_table + __chainhashfn((chain)))

static struct hlist_head chainhash_table[CHAINHASH_SIZE];

/*
 * the id of held_lock
 */

static inline u16 hlock_id(struct held_lock *hlock)
{
 BUILD_BUG_ON(MAX_LOCKDEP_KEYS_BITS + 2 > 16);

 return (hlock->class_idx | (hlock->read << MAX_LOCKDEP_KEYS_BITS));
}

static inline __maybe_unused unsigned int chain_hlock_class_idx(u16 hlock_id)
{
 return hlock_id & (MAX_LOCKDEP_KEYS - 1);
}

/*
 * The hash key of the lock dependency chains is a hash itself too:
 * it's a hash of all locks taken up to that lock, including that lock.
 * It's a 64-bit hash, because it's important for the keys to be
 * unique.
 */

static inline u64 iterate_chain_key(u64 key, u32 idx)
{
 u32 k0 = key, k1 = key >> 32;

 __jhash_mix(idx, k0, k1); /* Macro that modifies arguments! */

 return k0 | (u64)k1 << 32;
}

void lockdep_init_task(struct task_struct *task)
{
 task->lockdep_depth = 0; /* no locks held yet */
 task->curr_chain_key = INITIAL_CHAIN_KEY;
 task->lockdep_recursion = 0;
}

static __always_inline void lockdep_recursion_inc(void)
{
 __this_cpu_inc(lockdep_recursion);
}

static __always_inline void lockdep_recursion_finish(void)
{
 if (WARN_ON_ONCE(__this_cpu_dec_return(lockdep_recursion)))
  __this_cpu_write(lockdep_recursion, 0);
}

void lockdep_set_selftest_task(struct task_struct *task)
{
 lockdep_selftest_task_struct = task;
}

/*
 * Debugging switches:
 */


#define VERBOSE   0
#define VERY_VERBOSE  0

#if VERBOSE
define HARDIRQ_VERBOSE 1
define SOFTIRQ_VERBOSE 1
#else
define HARDIRQ_VERBOSE 0
define SOFTIRQ_VERBOSE 0
#endif

#if VERBOSE || HARDIRQ_VERBOSE || SOFTIRQ_VERBOSE
/*
 * Quick filtering for interesting events:
 */

static int class_filter(struct lock_class *class)
{
#if 0
 /* Example */
 if (class->name_version == 1 &&
   !strcmp(class->name, "lockname"))
  return 1;
 if (class->name_version == 1 &&
   !strcmp(class->name, "&struct->lockfield"))
  return 1;
#endif
 /* Filter everything else. 1 would be to allow everything else */
 return 0;
}
#endif

static int verbose(struct lock_class *class)
{
#if VERBOSE
 return class_filter(class);
#endif
 return 0;
}

static void print_lockdep_off(const char *bug_msg)
{
 printk(KERN_DEBUG "%s\n", bug_msg);
 printk(KERN_DEBUG "turning off the locking correctness validator.\n");
#ifdef CONFIG_LOCK_STAT
 printk(KERN_DEBUG "Please attach the output of /proc/lock_stat to the bug report\n");
#endif
}

unsigned long nr_stack_trace_entries;

#ifdef CONFIG_PROVE_LOCKING
/**
 * struct lock_trace - single stack backtrace
 * @hash_entry: Entry in a stack_trace_hash[] list.
 * @hash: jhash() of @entries.
 * @nr_entries: Number of entries in @entries.
 * @entries: Actual stack backtrace.
 */

struct lock_trace {
 struct hlist_node hash_entry;
 u32   hash;
 u32   nr_entries;
 unsigned long  entries[] __aligned(sizeof(unsigned long));
};
#define LOCK_TRACE_SIZE_IN_LONGS    \
 (sizeof(struct lock_trace) / sizeof(unsigned long))
/*
 * Stack-trace: sequence of lock_trace structures. Protected by the graph_lock.
 */

static unsigned long stack_trace[MAX_STACK_TRACE_ENTRIES];
static struct hlist_head stack_trace_hash[STACK_TRACE_HASH_SIZE];

static bool traces_identical(struct lock_trace *t1, struct lock_trace *t2)
{
 return t1->hash == t2->hash && t1->nr_entries == t2->nr_entries &&
  memcmp(t1->entries, t2->entries,
         t1->nr_entries * sizeof(t1->entries[0])) == 0;
}

static struct lock_trace *save_trace(void)
{
 struct lock_trace *trace, *t2;
 struct hlist_head *hash_head;
 u32 hash;
 int max_entries;

 BUILD_BUG_ON_NOT_POWER_OF_2(STACK_TRACE_HASH_SIZE);
 BUILD_BUG_ON(LOCK_TRACE_SIZE_IN_LONGS >= MAX_STACK_TRACE_ENTRIES);

 trace = (struct lock_trace *)(stack_trace + nr_stack_trace_entries);
 max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries -
  LOCK_TRACE_SIZE_IN_LONGS;

 if (max_entries <= 0) {
  if (!debug_locks_off_graph_unlock())
   return NULL;

  nbcon_cpu_emergency_enter();
  print_lockdep_off("BUG: MAX_STACK_TRACE_ENTRIES too low!");
  dump_stack();
  nbcon_cpu_emergency_exit();

  return NULL;
 }
 trace->nr_entries = stack_trace_save(trace->entries, max_entries, 3);

 hash = jhash(trace->entries, trace->nr_entries *
       sizeof(trace->entries[0]), 0);
 trace->hash = hash;
 hash_head = stack_trace_hash + (hash & (STACK_TRACE_HASH_SIZE - 1));
 hlist_for_each_entry(t2, hash_head, hash_entry) {
  if (traces_identical(trace, t2))
   return t2;
 }
 nr_stack_trace_entries += LOCK_TRACE_SIZE_IN_LONGS + trace->nr_entries;
 hlist_add_head(&trace->hash_entry, hash_head);

 return trace;
}

/* Return the number of stack traces in the stack_trace[] array. */
u64 lockdep_stack_trace_count(void)
{
 struct lock_trace *trace;
 u64 c = 0;
 int i;

 for (i = 0; i < ARRAY_SIZE(stack_trace_hash); i++) {
  hlist_for_each_entry(trace, &stack_trace_hash[i], hash_entry) {
   c++;
  }
 }

 return c;
}

/* Return the number of stack hash chains that have at least one stack trace. */
u64 lockdep_stack_hash_count(void)
{
 u64 c = 0;
 int i;

 for (i = 0; i < ARRAY_SIZE(stack_trace_hash); i++)
  if (!hlist_empty(&stack_trace_hash[i]))
   c++;

 return c;
}
#endif

unsigned int nr_hardirq_chains;
unsigned int nr_softirq_chains;
unsigned int nr_process_chains;
unsigned int max_lockdep_depth;

#ifdef CONFIG_DEBUG_LOCKDEP
/*
 * Various lockdep statistics:
 */

DEFINE_PER_CPU(struct lockdep_stats, lockdep_stats);
#endif

#ifdef CONFIG_PROVE_LOCKING
/*
 * Locking printouts:
 */


#define __USAGE(__STATE)      \
 [LOCK_USED_IN_##__STATE] = "IN-"__stringify(__STATE)"-W", \
 [LOCK_ENABLED_##__STATE] = __stringify(__STATE)"-ON-W",  \
 [LOCK_USED_IN_##__STATE##_READ] = "IN-"__stringify(__STATE)"-R",\
 [LOCK_ENABLED_##__STATE##_READ] = __stringify(__STATE)"-ON-R",

static const char *usage_str[] =
{
#define LOCKDEP_STATE(__STATE) __USAGE(__STATE)
#include "lockdep_states.h"
#undef LOCKDEP_STATE
 [LOCK_USED] = "INITIAL USE",
 [LOCK_USED_READ] = "INITIAL READ USE",
 /* abused as string storage for verify_lock_unused() */
 [LOCK_USAGE_STATES] = "IN-NMI",
};
#endif

const char *__get_key_name(const struct lockdep_subclass_key *key, char *str)
{
 return kallsyms_lookup((unsigned long)key, NULL, NULL, NULL, str);
}

static inline unsigned long lock_flag(enum lock_usage_bit bit)
{
 return 1UL << bit;
}

static char get_usage_char(struct lock_class *classenum lock_usage_bit bit)
{
 /*
 * The usage character defaults to '.' (i.e., irqs disabled and not in
 * irq context), which is the safest usage category.
 */

 char c = '.';

 /*
 * The order of the following usage checks matters, which will
 * result in the outcome character as follows:
 *
 * - '+': irq is enabled and not in irq context
 * - '-': in irq context and irq is disabled
 * - '?': in irq context and irq is enabled
 */

 if (class->usage_mask & lock_flag(bit + LOCK_USAGE_DIR_MASK)) {
  c = '+';
  if (class->usage_mask & lock_flag(bit))
   c = '?';
 } else if (class->usage_mask & lock_flag(bit))
  c = '-';

 return c;
}

void get_usage_chars(struct lock_class *classchar usage[LOCK_USAGE_CHARS])
{
 int i = 0;

#define LOCKDEP_STATE(__STATE)       \
 usage[i++] = get_usage_char(class, LOCK_USED_IN_##__STATE); \
 usage[i++] = get_usage_char(class, LOCK_USED_IN_##__STATE##_READ);
#include "lockdep_states.h"
#undef LOCKDEP_STATE

 usage[i] = '\0';
}

static void __print_lock_name(struct held_lock *hlock, struct lock_class *class)
{
 char str[KSYM_NAME_LEN];
 const char *name;

 name = class->name;
 if (!name) {
  name = __get_key_name(class->key, str);
  printk(KERN_CONT "%s", name);
 } else {
  printk(KERN_CONT "%s", name);
  if (class->name_version > 1)
   printk(KERN_CONT "#%d", class->name_version);
  if (class->subclass)
   printk(KERN_CONT "/%d", class->subclass);
  if (hlock && class->print_fn)
   class->print_fn(hlock->instance);
 }
}

static void print_lock_name(struct held_lock *hlock, struct lock_class *class)
{
 char usage[LOCK_USAGE_CHARS];

 get_usage_chars(class, usage);

 printk(KERN_CONT " (");
 __print_lock_name(hlock, class);
 printk(KERN_CONT "){%s}-{%d:%d}", usage,
   class->wait_type_outer ?: class->wait_type_inner,
   class->wait_type_inner);
}

static void print_lockdep_cache(struct lockdep_map *lock)
{
 const char *name;
 char str[KSYM_NAME_LEN];

 name = lock->name;
 if (!name)
  name = __get_key_name(lock->key->subkeys, str);

 printk(KERN_CONT "%s", name);
}

static void print_lock(struct held_lock *hlock)
{
 /*
 * We can be called locklessly through debug_show_all_locks() so be
 * extra careful, the hlock might have been released and cleared.
 *
 * If this indeed happens, lets pretend it does not hurt to continue
 * to print the lock unless the hlock class_idx does not point to a
 * registered class. The rationale here is: since we don't attempt
 * to distinguish whether we are in this situation, if it just
 * happened we can't count on class_idx to tell either.
 */

 struct lock_class *lock = hlock_class(hlock);

 if (!lock) {
  printk(KERN_CONT "\n");
  return;
 }

 printk(KERN_CONT "%px", hlock->instance);
 print_lock_name(hlock, lock);
 printk(KERN_CONT ", at: %pS\n", (void *)hlock->acquire_ip);
}

static void lockdep_print_held_locks(struct task_struct *p)
{
 int i, depth = READ_ONCE(p->lockdep_depth);

 if (!depth)
  printk("no locks held by %s/%d.\n", p->comm, task_pid_nr(p));
 else
  printk("%d lock%s held by %s/%d:\n", depth,
         str_plural(depth), p->comm, task_pid_nr(p));
 /*
 * It's not reliable to print a task's held locks if it's not sleeping
 * and it's not the current task.
 */

 if (p != current && task_is_running(p))
  return;
 for (i = 0; i < depth; i++) {
  printk(" #%d: ", i);
  print_lock(p->held_locks + i);
 }
}

static void print_kernel_ident(void)
{
 printk("%s %.*s %s\n", init_utsname()->release,
  (int)strcspn(init_utsname()->version, " "),
  init_utsname()->version,
  print_tainted());
}

static int very_verbose(struct lock_class *class)
{
#if VERY_VERBOSE
 return class_filter(class);
#endif
 return 0;
}

/*
 * Is this the address of a static object:
 */

#ifdef __KERNEL__
static int static_obj(const void *obj)
{
 unsigned long addr = (unsigned long) obj;

 if (is_kernel_core_data(addr))
  return 1;

 /*
 * keys are allowed in the __ro_after_init section.
 */

 if (is_kernel_rodata(addr))
  return 1;

 /*
 * in initdata section and used during bootup only?
 * NOTE: On some platforms the initdata section is
 * outside of the _stext ... _end range.
 */

 if (system_state < SYSTEM_FREEING_INITMEM &&
  init_section_contains((void *)addr, 1))
  return 1;

 /*
 * in-kernel percpu var?
 */

 if (is_kernel_percpu_address(addr))
  return 1;

 /*
 * module static or percpu var?
 */

 return is_module_address(addr) || is_module_percpu_address(addr);
}
#endif

/*
 * To make lock name printouts unique, we calculate a unique
 * class->name_version generation counter. The caller must hold the graph
 * lock.
 */

static int count_matching_names(struct lock_class *new_class)
{
 struct lock_class *class;
 int count = 0;

 if (!new_class->name)
  return 0;

 list_for_each_entry(class, &all_lock_classes, lock_entry) {
  if (new_class->key - new_class->subclass == class->key)
   return class->name_version;
  if (class->name && !strcmp(class->name, new_class->name))
   count = max(count, class->name_version);
 }

 return count + 1;
}

/* used from NMI context -- must be lockless */
static noinstr struct lock_class *
look_up_lock_class(const struct lockdep_map *lock, unsigned int subclass)
{
 struct lockdep_subclass_key *key;
 struct hlist_head *hash_head;
 struct lock_class *class;

 if (unlikely(subclass >= MAX_LOCKDEP_SUBCLASSES)) {
  instrumentation_begin();
  debug_locks_off();
  nbcon_cpu_emergency_enter();
  printk(KERN_ERR
   "BUG: looking up invalid subclass: %u\n", subclass);
  printk(KERN_ERR
   "turning off the locking correctness validator.\n");
  dump_stack();
  nbcon_cpu_emergency_exit();
  instrumentation_end();
  return NULL;
 }

 /*
 * If it is not initialised then it has never been locked,
 * so it won't be present in the hash table.
 */

 if (unlikely(!lock->key))
  return NULL;

 /*
 * NOTE: the class-key must be unique. For dynamic locks, a static
 * lock_class_key variable is passed in through the mutex_init()
 * (or spin_lock_init()) call - which acts as the key. For static
 * locks we use the lock object itself as the key.
 */

 BUILD_BUG_ON(sizeof(struct lock_class_key) >
   sizeof(struct lockdep_map));

 key = lock->key->subkeys + subclass;

 hash_head = classhashentry(key);

 /*
 * We do an RCU walk of the hash, see lockdep_free_key_range().
 */

 if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
  return NULL;

 hlist_for_each_entry_rcu_notrace(class, hash_head, hash_entry) {
  if (class->key == key) {
   /*
 * Huh! same key, different name? Did someone trample
 * on some memory? We're most confused.
 */

   WARN_ONCE(class->name != lock->name &&
      lock->key != &__lockdep_no_validate__,
      "Looking for class \"%s\" with key %ps, but found a different class \"%s\" with the same key\n",
      lock->name, lock->key, class->name);
   return class;
  }
 }

 return NULL;
}

/*
 * Static locks do not have their class-keys yet - for them the key is
 * the lock object itself. If the lock is in the per cpu area, the
 * canonical address of the lock (per cpu offset removed) is used.
 */

static bool assign_lock_key(struct lockdep_map *lock)
{
 unsigned long can_addr, addr = (unsigned long)lock;

#ifdef __KERNEL__
 /*
 * lockdep_free_key_range() assumes that struct lock_class_key
 * objects do not overlap. Since we use the address of lock
 * objects as class key for static objects, check whether the
 * size of lock_class_key objects does not exceed the size of
 * the smallest lock object.
 */

 BUILD_BUG_ON(sizeof(struct lock_class_key) > sizeof(raw_spinlock_t));
#endif

 if (__is_kernel_percpu_address(addr, &can_addr))
  lock->key = (void *)can_addr;
 else if (__is_module_percpu_address(addr, &can_addr))
  lock->key = (void *)can_addr;
 else if (static_obj(lock))
  lock->key = (void *)lock;
 else {
  /* Debug-check: all keys must be persistent! */
  debug_locks_off();
  nbcon_cpu_emergency_enter();
  pr_err("INFO: trying to register non-static key.\n");
  pr_err("The code is fine but needs lockdep annotation, or maybe\n");
  pr_err("you didn't initialize this object before use?\n");
  pr_err("turning off the locking correctness validator.\n");
  dump_stack();
  nbcon_cpu_emergency_exit();
  return false;
 }

 return true;
}

#ifdef CONFIG_DEBUG_LOCKDEP

/* Check whether element @e occurs in list @h */
static bool in_list(struct list_head *e, struct list_head *h)
{
 struct list_head *f;

 list_for_each(f, h) {
  if (e == f)
   return true;
 }

 return false;
}

/*
 * Check whether entry @e occurs in any of the locks_after or locks_before
 * lists.
 */

static bool in_any_class_list(struct list_head *e)
{
 struct lock_class *class;
 int i;

 for (i = 0; i < ARRAY_SIZE(lock_classes); i++) {
  class = &lock_classes[i];
  if (in_list(e, &class->locks_after) ||
      in_list(e, &class->locks_before))
   return true;
 }
 return false;
}

static bool class_lock_list_valid(struct lock_class *c, struct list_head *h)
{
 struct lock_list *e;

 list_for_each_entry(e, h, entry) {
  if (e->links_to != c) {
   printk(KERN_INFO "class %s: mismatch for lock entry %ld; class %s <> %s",
          c->name ? : "(?)",
          (unsigned long)(e - list_entries),
          e->links_to && e->links_to->name ?
          e->links_to->name : "(?)",
          e->class && e->class->name ? e->class->name :
          "(?)");
   return false;
  }
 }
 return true;
}

#ifdef CONFIG_PROVE_LOCKING
static u16 chain_hlocks[MAX_LOCKDEP_CHAIN_HLOCKS];
#endif

static bool check_lock_chain_key(struct lock_chain *chain)
{
#ifdef CONFIG_PROVE_LOCKING
 u64 chain_key = INITIAL_CHAIN_KEY;
 int i;

 for (i = chain->base; i < chain->base + chain->depth; i++)
  chain_key = iterate_chain_key(chain_key, chain_hlocks[i]);
 /*
 * The 'unsigned long long' casts avoid that a compiler warning
 * is reported when building tools/lib/lockdep.
 */

 if (chain->chain_key != chain_key) {
  printk(KERN_INFO "chain %lld: key %#llx <> %#llx\n",
         (unsigned long long)(chain - lock_chains),
         (unsigned long long)chain->chain_key,
         (unsigned long long)chain_key);
  return false;
 }
#endif
 return true;
}

static bool in_any_zapped_class_list(struct lock_class *class)
{
 struct pending_free *pf;
 int i;

 for (i = 0, pf = delayed_free.pf; i < ARRAY_SIZE(delayed_free.pf); i++, pf++) {
  if (in_list(&class->lock_entry, &pf->zapped))
   return true;
 }

 return false;
}

static bool __check_data_structures(void)
{
 struct lock_class *class;
 struct lock_chain *chain;
 struct hlist_head *head;
 struct lock_list *e;
 int i;

 /* Check whether all classes occur in a lock list. */
 for (i = 0; i < ARRAY_SIZE(lock_classes); i++) {
  class = &lock_classes[i];
  if (!in_list(&class->lock_entry, &all_lock_classes) &&
      !in_list(&class->lock_entry, &free_lock_classes) &&
      !in_any_zapped_class_list(class)) {
   printk(KERN_INFO "class %px/%s is not in any class list\n",
          class, class->name ? : "(?)");
   return false;
  }
 }

 /* Check whether all classes have valid lock lists. */
 for (i = 0; i < ARRAY_SIZE(lock_classes); i++) {
  class = &lock_classes[i];
  if (!class_lock_list_valid(class, &class->locks_before))
   return false;
  if (!class_lock_list_valid(class, &class->locks_after))
   return false;
 }

 /* Check the chain_key of all lock chains. */
 for (i = 0; i < ARRAY_SIZE(chainhash_table); i++) {
  head = chainhash_table + i;
  hlist_for_each_entry_rcu(chain, head, entry) {
   if (!check_lock_chain_key(chain))
    return false;
  }
 }

 /*
 * Check whether all list entries that are in use occur in a class
 * lock list.
 */

 for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
  e = list_entries + i;
  if (!in_any_class_list(&e->entry)) {
   printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
          (unsigned int)(e - list_entries),
          e->class->name ? : "(?)",
          e->links_to->name ? : "(?)");
   return false;
  }
 }

 /*
 * Check whether all list entries that are not in use do not occur in
 * a class lock list.
 */

 for_each_clear_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
  e = list_entries + i;
  if (in_any_class_list(&e->entry)) {
   printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
          (unsigned int)(e - list_entries),
          e->class && e->class->name ? e->class->name :
          "(?)",
          e->links_to && e->links_to->name ?
          e->links_to->name : "(?)");
   return false;
  }
 }

 return true;
}

int check_consistency = 0;
module_param(check_consistency, int, 0644);

static void check_data_structures(void)
{
 static bool once = false;

 if (check_consistency && !once) {
  if (!__check_data_structures()) {
   once = true;
   WARN_ON(once);
  }
 }
}

#else /* CONFIG_DEBUG_LOCKDEP */

static inline void check_data_structures(void) { }

#endif /* CONFIG_DEBUG_LOCKDEP */

static void init_chain_block_buckets(void);

/*
 * Initialize the lock_classes[] array elements, the free_lock_classes list
 * and also the delayed_free structure.
 */

static void init_data_structures_once(void)
{
 static bool __read_mostly ds_initialized, rcu_head_initialized;
 int i;

 if (likely(rcu_head_initialized))
  return;

 if (system_state >= SYSTEM_SCHEDULING) {
  init_rcu_head(&delayed_free.rcu_head);
  rcu_head_initialized = true;
 }

 if (ds_initialized)
  return;

 ds_initialized = true;

 INIT_LIST_HEAD(&delayed_free.pf[0].zapped);
 INIT_LIST_HEAD(&delayed_free.pf[1].zapped);

 for (i = 0; i < ARRAY_SIZE(lock_classes); i++) {
  list_add_tail(&lock_classes[i].lock_entry, &free_lock_classes);
  INIT_LIST_HEAD(&lock_classes[i].locks_after);
  INIT_LIST_HEAD(&lock_classes[i].locks_before);
 }
 init_chain_block_buckets();
}

static inline struct hlist_head *keyhashentry(const struct lock_class_key *key)
{
 unsigned long hash = hash_long((uintptr_t)key, KEYHASH_BITS);

 return lock_keys_hash + hash;
}

/* Register a dynamically allocated key. */
void lockdep_register_key(struct lock_class_key *key)
{
 struct hlist_head *hash_head;
 struct lock_class_key *k;
 unsigned long flags;

 if (WARN_ON_ONCE(static_obj(key)))
  return;
 hash_head = keyhashentry(key);

 raw_local_irq_save(flags);
 if (!graph_lock())
  goto restore_irqs;
 hlist_for_each_entry_rcu(k, hash_head, hash_entry) {
  if (WARN_ON_ONCE(k == key))
   goto out_unlock;
 }
 hlist_add_head_rcu(&key->hash_entry, hash_head);
 nr_dynamic_keys++;
out_unlock:
 graph_unlock();
restore_irqs:
 raw_local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(lockdep_register_key);

/* Check whether a key has been registered as a dynamic key. */
static bool is_dynamic_key(const struct lock_class_key *key)
{
 struct hlist_head *hash_head;
 struct lock_class_key *k;
 bool found = false;

 if (WARN_ON_ONCE(static_obj(key)))
  return false;

 /*
 * If lock debugging is disabled lock_keys_hash[] may contain
 * pointers to memory that has already been freed. Avoid triggering
 * a use-after-free in that case by returning early.
 */

 if (!debug_locks)
  return true;

 hash_head = keyhashentry(key);

 rcu_read_lock();
 hlist_for_each_entry_rcu(k, hash_head, hash_entry) {
  if (k == key) {
   found = true;
   break;
  }
 }
 rcu_read_unlock();

 return found;
}

/*
 * Register a lock's class in the hash-table, if the class is not present
 * yet. Otherwise we look it up. We cache the result in the lock object
 * itself, so actual lookup of the hash should be once per lock object.
 */

static struct lock_class *
register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
{
 struct lockdep_subclass_key *key;
 struct hlist_head *hash_head;
 struct lock_class *class;
 int idx;

 DEBUG_LOCKS_WARN_ON(!irqs_disabled());

 class = look_up_lock_class(lock, subclass);
 if (likely(class))
  goto out_set_class_cache;

 if (!lock->key) {
  if (!assign_lock_key(lock))
   return NULL;
 } else if (!static_obj(lock->key) && !is_dynamic_key(lock->key)) {
  return NULL;
 }

 key = lock->key->subkeys + subclass;
 hash_head = classhashentry(key);

 if (!graph_lock()) {
  return NULL;
 }
 /*
 * We have to do the hash-walk again, to avoid races
 * with another CPU:
 */

 hlist_for_each_entry_rcu(class, hash_head, hash_entry) {
  if (class->key == key)
   goto out_unlock_set;
 }

 init_data_structures_once();

 /* Allocate a new lock class and add it to the hash. */
 class = list_first_entry_or_null(&free_lock_classes, typeof(*class),
      lock_entry);
 if (!class) {
  if (!debug_locks_off_graph_unlock()) {
   return NULL;
  }

  nbcon_cpu_emergency_enter();
  print_lockdep_off("BUG: MAX_LOCKDEP_KEYS too low!");
  dump_stack();
  nbcon_cpu_emergency_exit();
  return NULL;
 }
 nr_lock_classes++;
 __set_bit(class - lock_classes, lock_classes_in_use);
 debug_atomic_inc(nr_unused_locks);
 class->key = key;
 class->name = lock->name;
 class->subclass = subclass;
 WARN_ON_ONCE(!list_empty(&class->locks_before));
 WARN_ON_ONCE(!list_empty(&class->locks_after));
 class->name_version = count_matching_names(class);
 class->wait_type_inner = lock->wait_type_inner;
 class->wait_type_outer = lock->wait_type_outer;
 class->lock_type = lock->lock_type;
 /*
 * We use RCU's safe list-add method to make
 * parallel walking of the hash-list safe:
 */

 hlist_add_head_rcu(&class->hash_entry, hash_head);
 /*
 * Remove the class from the free list and add it to the global list
 * of classes.
 */

 list_move_tail(&class->lock_entry, &all_lock_classes);
 idx = class - lock_classes;
 if (idx > max_lock_class_idx)
  max_lock_class_idx = idx;

 if (verbose(class)) {
  graph_unlock();

  nbcon_cpu_emergency_enter();
  printk("\nnew class %px: %s", class->key, class->name);
  if (class->name_version > 1)
   printk(KERN_CONT "#%d", class->name_version);
  printk(KERN_CONT "\n");
  dump_stack();
  nbcon_cpu_emergency_exit();

  if (!graph_lock()) {
   return NULL;
  }
 }
out_unlock_set:
 graph_unlock();

out_set_class_cache:
 if (!subclass || force)
  lock->class_cache[0] = class;
 else if (subclass < NR_LOCKDEP_CACHING_CLASSES)
  lock->class_cache[subclass] = class;

 /*
 * Hash collision, did we smoke some? We found a class with a matching
 * hash but the subclass -- which is hashed in -- didn't match.
 */

 if (DEBUG_LOCKS_WARN_ON(class->subclass != subclass))
  return NULL;

 return class;
}

#ifdef CONFIG_PROVE_LOCKING
/*
 * Allocate a lockdep entry. (assumes the graph_lock held, returns
 * with NULL on failure)
 */

static struct lock_list *alloc_list_entry(void)
{
 int idx = find_first_zero_bit(list_entries_in_use,
          ARRAY_SIZE(list_entries));

 if (idx >= ARRAY_SIZE(list_entries)) {
  if (!debug_locks_off_graph_unlock())
   return NULL;

  nbcon_cpu_emergency_enter();
  print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low!");
  dump_stack();
  nbcon_cpu_emergency_exit();
  return NULL;
 }
 nr_list_entries++;
 __set_bit(idx, list_entries_in_use);
 return list_entries + idx;
}

/*
 * Add a new dependency to the head of the list:
 */

static int add_lock_to_list(struct lock_class *this,
       struct lock_class *links_to, struct list_head *head,
       u16 distance, u8 dep,
       const struct lock_trace *trace)
{
 struct lock_list *entry;
 /*
 * Lock not present yet - get a new dependency struct and
 * add it to the list:
 */

 entry = alloc_list_entry();
 if (!entry)
  return 0;

 entry->class = this;
 entry->links_to = links_to;
 entry->dep = dep;
 entry->distance = distance;
 entry->trace = trace;
 /*
 * Both allocation and removal are done under the graph lock; but
 * iteration is under RCU-sched; see look_up_lock_class() and
 * lockdep_free_key_range().
 */

 list_add_tail_rcu(&entry->entry, head);

 return 1;
}

/*
 * For good efficiency of modular, we use power of 2
 */

#define MAX_CIRCULAR_QUEUE_SIZE  (1UL << CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS)
#define CQ_MASK    (MAX_CIRCULAR_QUEUE_SIZE-1)

/*
 * The circular_queue and helpers are used to implement graph
 * breadth-first search (BFS) algorithm, by which we can determine
 * whether there is a path from a lock to another. In deadlock checks,
 * a path from the next lock to be acquired to a previous held lock
 * indicates that adding the <prev> -> <next> lock dependency will
 * produce a circle in the graph. Breadth-first search instead of
 * depth-first search is used in order to find the shortest (circular)
 * path.
 */

struct circular_queue {
 struct lock_list *element[MAX_CIRCULAR_QUEUE_SIZE];
 unsigned int  front, rear;
};

static struct circular_queue lock_cq;

unsigned int max_bfs_queue_depth;

static unsigned int lockdep_dependency_gen_id;

static inline void __cq_init(struct circular_queue *cq)
{
 cq->front = cq->rear = 0;
 lockdep_dependency_gen_id++;
}

static inline int __cq_empty(struct circular_queue *cq)
{
 return (cq->front == cq->rear);
}

static inline int __cq_full(struct circular_queue *cq)
{
 return ((cq->rear + 1) & CQ_MASK) == cq->front;
}

static inline int __cq_enqueue(struct circular_queue *cq, struct lock_list *elem)
{
 if (__cq_full(cq))
  return -1;

 cq->element[cq->rear] = elem;
 cq->rear = (cq->rear + 1) & CQ_MASK;
 return 0;
}

/*
 * Dequeue an element from the circular_queue, return a lock_list if
 * the queue is not empty, or NULL if otherwise.
 */

static inline struct lock_list * __cq_dequeue(struct circular_queue *cq)
{
 struct lock_list * lock;

 if (__cq_empty(cq))
  return NULL;

 lock = cq->element[cq->front];
 cq->front = (cq->front + 1) & CQ_MASK;

 return lock;
}

static inline unsigned int  __cq_get_elem_count(struct circular_queue *cq)
{
 return (cq->rear - cq->front) & CQ_MASK;
}

static inline void mark_lock_accessed(struct lock_list *lock)
{
 lock->class->dep_gen_id = lockdep_dependency_gen_id;
}

static inline void visit_lock_entry(struct lock_list *lock,
        struct lock_list *parent)
{
 lock->parent = parent;
}

static inline unsigned long lock_accessed(struct lock_list *lock)
{
 return lock->class->dep_gen_id == lockdep_dependency_gen_id;
}

static inline struct lock_list *get_lock_parent(struct lock_list *child)
{
 return child->parent;
}

static inline int get_lock_depth(struct lock_list *child)
{
 int depth = 0;
 struct lock_list *parent;

 while ((parent = get_lock_parent(child))) {
  child = parent;
  depth++;
 }
 return depth;
}

/*
 * Return the forward or backward dependency list.
 *
 * @lock:   the lock_list to get its class's dependency list
 * @offset: the offset to struct lock_class to determine whether it is
 *          locks_after or locks_before
 */

static inline struct list_head *get_dep_list(struct lock_list *lock, int offset)
{
 void *lock_class = lock->class;

 return lock_class + offset;
}
/*
 * Return values of a bfs search:
 *
 * BFS_E* indicates an error
 * BFS_R* indicates a result (match or not)
 *
 * BFS_EINVALIDNODE: Find a invalid node in the graph.
 *
 * BFS_EQUEUEFULL: The queue is full while doing the bfs.
 *
 * BFS_RMATCH: Find the matched node in the graph, and put that node into
 *             *@target_entry.
 *
 * BFS_RNOMATCH: Haven't found the matched node and keep *@target_entry
 *               _unchanged_.
 */

enum bfs_result {
 BFS_EINVALIDNODE = -2,
 BFS_EQUEUEFULL = -1,
 BFS_RMATCH = 0,
 BFS_RNOMATCH = 1,
};

/*
 * bfs_result < 0 means error
 */

static inline bool bfs_error(enum bfs_result res)
{
 return res < 0;
}

/*
 * DEP_*_BIT in lock_list::dep
 *
 * For dependency @prev -> @next:
 *
 *   SR: @prev is shared reader (->read != 0) and @next is recursive reader
 *       (->read == 2)
 *   ER: @prev is exclusive locker (->read == 0) and @next is recursive reader
 *   SN: @prev is shared reader and @next is non-recursive locker (->read != 2)
 *   EN: @prev is exclusive locker and @next is non-recursive locker
 *
 * Note that we define the value of DEP_*_BITs so that:
 *   bit0 is prev->read == 0
 *   bit1 is next->read != 2
 */

#define DEP_SR_BIT (0 + (0 << 1)) /* 0 */
#define DEP_ER_BIT (1 + (0 << 1)) /* 1 */
#define DEP_SN_BIT (0 + (1 << 1)) /* 2 */
#define DEP_EN_BIT (1 + (1 << 1)) /* 3 */

#define DEP_SR_MASK (1U << (DEP_SR_BIT))
#define DEP_ER_MASK (1U << (DEP_ER_BIT))
#define DEP_SN_MASK (1U << (DEP_SN_BIT))
#define DEP_EN_MASK (1U << (DEP_EN_BIT))

static inline unsigned int
__calc_dep_bit(struct held_lock *prev, struct held_lock *next)
{
 return (prev->read == 0) + ((next->read != 2) << 1);
}

static inline u8 calc_dep(struct held_lock *prev, struct held_lock *next)
{
 return 1U << __calc_dep_bit(prev, next);
}

/*
 * calculate the dep_bit for backwards edges. We care about whether @prev is
 * shared and whether @next is recursive.
 */

static inline unsigned int
__calc_dep_bitb(struct held_lock *prev, struct held_lock *next)
{
 return (next->read != 2) + ((prev->read == 0) << 1);
}

static inline u8 calc_depb(struct held_lock *prev, struct held_lock *next)
{
 return 1U << __calc_dep_bitb(prev, next);
}

/*
 * Initialize a lock_list entry @lock belonging to @class as the root for a BFS
 * search.
 */

static inline void __bfs_init_root(struct lock_list *lock,
       struct lock_class *class)
{
 lock->class = class;
 lock->parent = NULL;
 lock->only_xr = 0;
}

/*
 * Initialize a lock_list entry @lock based on a lock acquisition @hlock as the
 * root for a BFS search.
 *
 * ->only_xr of the initial lock node is set to @hlock->read == 2, to make sure
 * that <prev> -> @hlock and @hlock -> <whatever __bfs() found> is not -(*R)->
 * and -(S*)->.
 */

static inline void bfs_init_root(struct lock_list *lock,
     struct held_lock *hlock)
{
 __bfs_init_root(lock, hlock_class(hlock));
 lock->only_xr = (hlock->read == 2);
}

/*
 * Similar to bfs_init_root() but initialize the root for backwards BFS.
 *
 * ->only_xr of the initial lock node is set to @hlock->read != 0, to make sure
 * that <next> -> @hlock and @hlock -> <whatever backwards BFS found> is not
 * -(*S)-> and -(R*)-> (reverse order of -(*R)-> and -(S*)->).
 */

static inline void bfs_init_rootb(struct lock_list *lock,
      struct held_lock *hlock)
{
 __bfs_init_root(lock, hlock_class(hlock));
 lock->only_xr = (hlock->read != 0);
}

static inline struct lock_list *__bfs_next(struct lock_list *lock, int offset)
{
 if (!lock || !lock->parent)
  return NULL;

 return list_next_or_null_rcu(get_dep_list(lock->parent, offset),
         &lock->entry, struct lock_list, entry);
}

/*
 * Breadth-First Search to find a strong path in the dependency graph.
 *
 * @source_entry: the source of the path we are searching for.
 * @data: data used for the second parameter of @match function
 * @match: match function for the search
 * @target_entry: pointer to the target of a matched path
 * @offset: the offset to struct lock_class to determine whether it is
 *          locks_after or locks_before
 *
 * We may have multiple edges (considering different kinds of dependencies,
 * e.g. ER and SN) between two nodes in the dependency graph. But
 * only the strong dependency path in the graph is relevant to deadlocks. A
 * strong dependency path is a dependency path that doesn't have two adjacent
 * dependencies as -(*R)-> -(S*)->, please see:
 *
 *         Documentation/locking/lockdep-design.rst
 *
 * for more explanation of the definition of strong dependency paths
 *
 * In __bfs(), we only traverse in the strong dependency path:
 *
 *     In lock_list::only_xr, we record whether the previous dependency only
 *     has -(*R)-> in the search, and if it does (prev only has -(*R)->), we
 *     filter out any -(S*)-> in the current dependency and after that, the
 *     ->only_xr is set according to whether we only have -(*R)-> left.
 */

static enum bfs_result __bfs(struct lock_list *source_entry,
        void *data,
        bool (*match)(struct lock_list *entry, void *data),
        bool (*skip)(struct lock_list *entry, void *data),
        struct lock_list **target_entry,
        int offset)
{
 struct circular_queue *cq = &lock_cq;
 struct lock_list *lock = NULL;
 struct lock_list *entry;
 struct list_head *head;
 unsigned int cq_depth;
 bool first;

 lockdep_assert_locked();

 __cq_init(cq);
 __cq_enqueue(cq, source_entry);

 while ((lock = __bfs_next(lock, offset)) || (lock = __cq_dequeue(cq))) {
  if (!lock->class)
   return BFS_EINVALIDNODE;

  /*
 * Step 1: check whether we already finish on this one.
 *
 * If we have visited all the dependencies from this @lock to
 * others (iow, if we have visited all lock_list entries in
 * @lock->class->locks_{after,before}) we skip, otherwise go
 * and visit all the dependencies in the list and mark this
 * list accessed.
 */

  if (lock_accessed(lock))
   continue;
  else
   mark_lock_accessed(lock);

  /*
 * Step 2: check whether prev dependency and this form a strong
 *         dependency path.
 */

  if (lock->parent) { /* Parent exists, check prev dependency */
   u8 dep = lock->dep;
   bool prev_only_xr = lock->parent->only_xr;

   /*
 * Mask out all -(S*)-> if we only have *R in previous
 * step, because -(*R)-> -(S*)-> don't make up a strong
 * dependency.
 */

   if (prev_only_xr)
    dep &= ~(DEP_SR_MASK | DEP_SN_MASK);

   /* If nothing left, we skip */
   if (!dep)
    continue;

   /* If there are only -(*R)-> left, set that for the next step */
   lock->only_xr = !(dep & (DEP_SN_MASK | DEP_EN_MASK));
  }

  /*
 * Step 3: we haven't visited this and there is a strong
 *         dependency path to this, so check with @match.
 *         If @skip is provide and returns true, we skip this
 *         lock (and any path this lock is in).
 */

  if (skip && skip(lock, data))
   continue;

  if (match(lock, data)) {
   *target_entry = lock;
   return BFS_RMATCH;
  }

  /*
 * Step 4: if not match, expand the path by adding the
 *         forward or backwards dependencies in the search
 *
 */

  first = true;
  head = get_dep_list(lock, offset);
  list_for_each_entry_rcu(entry, head, entry) {
   visit_lock_entry(entry, lock);

   /*
 * Note we only enqueue the first of the list into the
 * queue, because we can always find a sibling
 * dependency from one (see __bfs_next()), as a result
 * the space of queue is saved.
 */

   if (!first)
    continue;

   first = false;

   if (__cq_enqueue(cq, entry))
    return BFS_EQUEUEFULL;

   cq_depth = __cq_get_elem_count(cq);
   if (max_bfs_queue_depth < cq_depth)
    max_bfs_queue_depth = cq_depth;
  }
 }

 return BFS_RNOMATCH;
}

static inline enum bfs_result
__bfs_forwards(struct lock_list *src_entry,
        void *data,
        bool (*match)(struct lock_list *entry, void *data),
        bool (*skip)(struct lock_list *entry, void *data),
        struct lock_list **target_entry)
{
 return __bfs(src_entry, data, match, skip, target_entry,
       offsetof(struct lock_class, locks_after));

}

static inline enum bfs_result
__bfs_backwards(struct lock_list *src_entry,
  void *data,
  bool (*match)(struct lock_list *entry, void *data),
        bool (*skip)(struct lock_list *entry, void *data),
  struct lock_list **target_entry)
{
 return __bfs(src_entry, data, match, skip, target_entry,
       offsetof(struct lock_class, locks_before));

}

static void print_lock_trace(const struct lock_trace *trace,
        unsigned int spaces)
{
 stack_trace_print(trace->entries, trace->nr_entries, spaces);
}

/*
 * Print a dependency chain entry (this is only done when a deadlock
 * has been detected):
 */

static noinline void
print_circular_bug_entry(struct lock_list *target, int depth)
{
 if (debug_locks_silent)
  return;
 printk("\n-> #%u", depth);
 print_lock_name(NULL, target->class);
 printk(KERN_CONT ":\n");
 print_lock_trace(target->trace, 6);
}

static void
print_circular_lock_scenario(struct held_lock *src,
        struct held_lock *tgt,
        struct lock_list *prt)
{
 struct lock_class *source = hlock_class(src);
 struct lock_class *target = hlock_class(tgt);
 struct lock_class *parent = prt->class;
 int src_read = src->read;
 int tgt_read = tgt->read;

 /*
 * A direct locking problem where unsafe_class lock is taken
 * directly by safe_class lock, then all we need to show
 * is the deadlock scenario, as it is obvious that the
 * unsafe lock is taken under the safe lock.
 *
 * But if there is a chain instead, where the safe lock takes
 * an intermediate lock (middle_class) where this lock is
 * not the same as the safe lock, then the lock chain is
 * used to describe the problem. Otherwise we would need
 * to show a different CPU case for each link in the chain
 * from the safe_class lock to the unsafe_class lock.
 */

 if (parent != source) {
  printk("Chain exists of:\n ");
  __print_lock_name(src, source);
  printk(KERN_CONT " --> ");
  __print_lock_name(NULL, parent);
  printk(KERN_CONT " --> ");
  __print_lock_name(tgt, target);
  printk(KERN_CONT "\n\n");
 }

 printk(" Possible unsafe locking scenario:\n\n");
 printk(" CPU0 CPU1\n");
 printk(" ---- ----\n");
 if (tgt_read != 0)
  printk(" rlock(");
 else
  printk(" lock(");
 __print_lock_name(tgt, target);
 printk(KERN_CONT ");\n");
 printk(" lock(");
 __print_lock_name(NULL, parent);
 printk(KERN_CONT ");\n");
 printk(" lock(");
 __print_lock_name(tgt, target);
 printk(KERN_CONT ");\n");
 if (src_read != 0)
  printk(" rlock(");
 else if (src->sync)
  printk(" sync(");
 else
  printk(" lock(");
 __print_lock_name(src, source);
 printk(KERN_CONT ");\n");
 printk("\n *** DEADLOCK ***\n\n");
}

/*
 * When a circular dependency is detected, print the
 * header first:
 */

static noinline void
print_circular_bug_header(struct lock_list *entry, unsigned int depth,
   struct held_lock *check_src,
   struct held_lock *check_tgt)
{
 struct task_struct *curr = current;

 if (debug_locks_silent)
  return;

 pr_warn("\n");
 pr_warn("======================================================\n");
 pr_warn("WARNING: possible circular locking dependency detected\n");
 print_kernel_ident();
 pr_warn("------------------------------------------------------\n");
 pr_warn("%s/%d is trying to acquire lock:\n",
  curr->comm, task_pid_nr(curr));
 print_lock(check_src);

 pr_warn("\nbut task is already holding lock:\n");

 print_lock(check_tgt);
 pr_warn("\nwhich lock already depends on the new lock.\n\n");
 pr_warn("\nthe existing dependency chain (in reverse order) is:\n");

 print_circular_bug_entry(entry, depth);
}

/*
 * We are about to add B -> A into the dependency graph, and in __bfs() a
 * strong dependency path A -> .. -> B is found: hlock_class equals
 * entry->class.
 *
 * We will have a deadlock case (conflict) if A -> .. -> B -> A is a strong
 * dependency cycle, that means:
 *
 * Either
 *
 *     a) B -> A is -(E*)->
 *
 * or
 *
 *     b) A -> .. -> B is -(*N)-> (i.e. A -> .. -(*N)-> B)
 *
 * as then we don't have -(*R)-> -(S*)-> in the cycle.
 */

static inline bool hlock_conflict(struct lock_list *entry, void *data)
{
 struct held_lock *hlock = (struct held_lock *)data;

 return hlock_class(hlock) == entry->class && /* Found A -> .. -> B */
        (hlock->read == 0 || /* B -> A is -(E*)-> */
  !entry->only_xr); /* A -> .. -> B is -(*N)-> */
}

static noinline void print_circular_bug(struct lock_list *this,
    struct lock_list *target,
    struct held_lock *check_src,
    struct held_lock *check_tgt)
{
 struct task_struct *curr = current;
 struct lock_list *parent;
 struct lock_list *first_parent;
 int depth;

 if (!debug_locks_off_graph_unlock() || debug_locks_silent)
  return;

 this->trace = save_trace();
 if (!this->trace)
  return;

 depth = get_lock_depth(target);

 nbcon_cpu_emergency_enter();

 print_circular_bug_header(target, depth, check_src, check_tgt);

 parent = get_lock_parent(target);
 first_parent = parent;

 while (parent) {
  print_circular_bug_entry(parent, --depth);
  parent = get_lock_parent(parent);
 }

 printk("\nother info that might help us debug this:\n\n");
 print_circular_lock_scenario(check_src, check_tgt,
         first_parent);

 lockdep_print_held_locks(curr);

 printk("\nstack backtrace:\n");
 dump_stack();

 nbcon_cpu_emergency_exit();
}

static noinline void print_bfs_bug(int ret)
{
 if (!debug_locks_off_graph_unlock())
  return;

 /*
 * Breadth-first-search failed, graph got corrupted?
 */

 if (ret == BFS_EQUEUEFULL)
  pr_warn("Increase LOCKDEP_CIRCULAR_QUEUE_BITS to avoid this warning:\n");

 WARN(1, "lockdep bfs error:%d\n", ret);
}

static bool noop_count(struct lock_list *entry, void *data)
{
 (*(unsigned long *)data)++;
 return false;
}

static unsigned long __lockdep_count_forward_deps(struct lock_list *this)
{
 unsigned long  count = 0;
 struct lock_list *target_entry;

 __bfs_forwards(this, (void *)&count, noop_count, NULL, &target_entry);

 return count;
}
unsigned long lockdep_count_forward_deps(struct lock_class *class)
{
 unsigned long ret, flags;
 struct lock_list this;

 __bfs_init_root(&thisclass);

 raw_local_irq_save(flags);
 lockdep_lock();
 ret = __lockdep_count_forward_deps(&this);
 lockdep_unlock();
 raw_local_irq_restore(flags);

 return ret;
}

static unsigned long __lockdep_count_backward_deps(struct lock_list *this)
{
 unsigned long  count = 0;
 struct lock_list *target_entry;

 __bfs_backwards(this, (void *)&count, noop_count, NULL, &target_entry);

 return count;
}

unsigned long lockdep_count_backward_deps(struct lock_class *class)
{
 unsigned long ret, flags;
 struct lock_list this;

 __bfs_init_root(&thisclass);

 raw_local_irq_save(flags);
 lockdep_lock();
 ret = __lockdep_count_backward_deps(&this);
 lockdep_unlock();
 raw_local_irq_restore(flags);

 return ret;
}

/*
 * Check that the dependency graph starting at <src> can lead to
 * <target> or not.
 */

static noinline enum bfs_result
check_path(struct held_lock *target, struct lock_list *src_entry,
    bool (*match)(struct lock_list *entry, void *data),
    bool (*skip)(struct lock_list *entry, void *data),
    struct lock_list **target_entry)
{
 enum bfs_result ret;

 ret = __bfs_forwards(src_entry, target, match, skip, target_entry);

 if (unlikely(bfs_error(ret)))
  print_bfs_bug(ret);

 return ret;
}

static void print_deadlock_bug(struct task_struct *, struct held_lock *, struct held_lock *);

/*
 * Prove that the dependency graph starting at <src> can not
 * lead to <target>. If it can, there is a circle when adding
 * <target> -> <src> dependency.
 *
 * Print an error and return BFS_RMATCH if it does.
 */

static noinline enum bfs_result
check_noncircular(struct held_lock *src, struct held_lock *target,
    struct lock_trace **const trace)
{
 enum bfs_result ret;
 struct lock_list *target_entry;
 struct lock_list src_entry;

 bfs_init_root(&src_entry, src);

 debug_atomic_inc(nr_cyclic_checks);

 ret = check_path(target, &src_entry, hlock_conflict, NULL, &target_entry);

 if (unlikely(ret == BFS_RMATCH)) {
  if (!*trace) {
   /*
 * If save_trace fails here, the printing might
 * trigger a WARN but because of the !nr_entries it
 * should not do bad things.
 */

   *trace = save_trace();
  }

  if (src->class_idx == target->class_idx)
   print_deadlock_bug(current, src, target);
  else
   print_circular_bug(&src_entry, target_entry, src, target);
 }

 return ret;
}

#ifdef CONFIG_TRACE_IRQFLAGS

/*
 * Forwards and backwards subgraph searching, for the purposes of
 * proving that two subgraphs can be connected by a new dependency
 * without creating any illegal irq-safe -> irq-unsafe lock dependency.
 *
 * A irq safe->unsafe deadlock happens with the following conditions:
 *
 * 1) We have a strong dependency path A -> ... -> B
 *
 * 2) and we have ENABLED_IRQ usage of B and USED_IN_IRQ usage of A, therefore
 *    irq can create a new dependency B -> A (consider the case that a holder
 *    of B gets interrupted by an irq whose handler will try to acquire A).
 *
 * 3) the dependency circle A -> ... -> B -> A we get from 1) and 2) is a
 *    strong circle:
 *
 *      For the usage bits of B:
 *        a) if A -> B is -(*N)->, then B -> A could be any type, so any
 *           ENABLED_IRQ usage suffices.
 *        b) if A -> B is -(*R)->, then B -> A must be -(E*)->, so only
 *           ENABLED_IRQ_*_READ usage suffices.
 *
 *      For the usage bits of A:
 *        c) if A -> B is -(E*)->, then B -> A could be any type, so any
 *           USED_IN_IRQ usage suffices.
 *        d) if A -> B is -(S*)->, then B -> A must be -(*N)->, so only
 *           USED_IN_IRQ_*_READ usage suffices.
 */


/*
 * There is a strong dependency path in the dependency graph: A -> B, and now
 * we need to decide which usage bit of A should be accumulated to detect
 * safe->unsafe bugs.
 *
 * Note that usage_accumulate() is used in backwards search, so ->only_xr
 * stands for whether A -> B only has -(S*)-> (in this case ->only_xr is true).
 *
 * As above, if only_xr is false, which means A -> B has -(E*)-> dependency
 * path, any usage of A should be considered. Otherwise, we should only
 * consider _READ usage.
 */

static inline bool usage_accumulate(struct lock_list *entry, void *mask)
{
 if (!entry->only_xr)
  *(unsigned long *)mask |= entry->class->usage_mask;
 else /* Mask out _READ usage bits */
  *(unsigned long *)mask |= (entry->class->usage_mask & LOCKF_IRQ);

 return false;
}

/*
 * There is a strong dependency path in the dependency graph: A -> B, and now
 * we need to decide which usage bit of B conflicts with the usage bits of A,
 * i.e. which usage bit of B may introduce safe->unsafe deadlocks.
 *
 * As above, if only_xr is false, which means A -> B has -(*N)-> dependency
 * path, any usage of B should be considered. Otherwise, we should only
 * consider _READ usage.
 */

static inline bool usage_match(struct lock_list *entry, void *mask)
{
 if (!entry->only_xr)
  return !!(entry->class->usage_mask & *(unsigned long *)mask);
 else /* Mask out _READ usage bits */
  return !!((entry->class->usage_mask & LOCKF_IRQ) & *(unsigned long *)mask);
}

static inline bool usage_skip(struct lock_list *entry, void *mask)
{
 if (entry->class->lock_type == LD_LOCK_NORMAL)
  return false;

 /*
 * Skip local_lock() for irq inversion detection.
 *
 * For !RT, local_lock() is not a real lock, so it won't carry any
 * dependency.
 *
 * For RT, an irq inversion happens when we have lock A and B, and on
 * some CPU we can have:
 *
 * lock(A);
 * <interrupted>
 *   lock(B);
 *
 * where lock(B) cannot sleep, and we have a dependency B -> ... -> A.
 *
 * Now we prove local_lock() cannot exist in that dependency. First we
 * have the observation for any lock chain L1 -> ... -> Ln, for any
 * 1 <= i <= n, Li.inner_wait_type <= L1.inner_wait_type, otherwise
 * wait context check will complain. And since B is not a sleep lock,
 * therefore B.inner_wait_type >= 2, and since the inner_wait_type of
 * local_lock() is 3, which is greater than 2, therefore there is no
 * way the local_lock() exists in the dependency B -> ... -> A.
 *
 * As a result, we will skip local_lock(), when we search for irq
 * inversion bugs.
 */

 if (entry->class->lock_type == LD_LOCK_PERCPU &&
     DEBUG_LOCKS_WARN_ON(entry->class->wait_type_inner < LD_WAIT_CONFIG))
  return false;

 /*
 * Skip WAIT_OVERRIDE for irq inversion detection -- it's not actually
 * a lock and only used to override the wait_type.
 */


 return true;
}

/*
 * Find a node in the forwards-direction dependency sub-graph starting
 * at @root->class that matches @bit.
 *
 * Return BFS_MATCH if such a node exists in the subgraph, and put that node
 * into *@target_entry.
 */

static enum bfs_result
find_usage_forwards(struct lock_list *root, unsigned long usage_mask,
   struct lock_list **target_entry)
{
 enum bfs_result result;

 debug_atomic_inc(nr_find_usage_forwards_checks);

 result = __bfs_forwards(root, &usage_mask, usage_match, usage_skip, target_entry);

 return result;
}

/*
 * Find a node in the backwards-direction dependency sub-graph starting
 * at @root->class that matches @bit.
 */

static enum bfs_result
find_usage_backwards(struct lock_list *root, unsigned long usage_mask,
   struct lock_list **target_entry)
{
 enum bfs_result result;

 debug_atomic_inc(nr_find_usage_backwards_checks);

 result = __bfs_backwards(root, &usage_mask, usage_match, usage_skip, target_entry);

 return result;
}

static void print_lock_class_header(struct lock_class *classint depth)
{
 int bit;

 printk("%*s->", depth, "");
 print_lock_name(NULL, class);
#ifdef CONFIG_DEBUG_LOCKDEP
 printk(KERN_CONT " ops: %lu", debug_class_ops_read(class));
#endif
 printk(KERN_CONT " {\n");

 for (bit = 0; bit < LOCK_TRACE_STATES; bit++) {
  if (class->usage_mask & (1 << bit)) {
   int len = depth;

   len += printk("%*s %s", depth, "", usage_str[bit]);
   len += printk(KERN_CONT " at:\n");
   print_lock_trace(class->usage_traces[bit], len);
  }
 }
 printk("%*s }\n", depth, "");

 printk("%*s ... key at: [<%px>] %pS\n",
  depth, "", class->key, class->key);
}

/*
 * Dependency path printing:
 *
 * After BFS we get a lock dependency path (linked via ->parent of lock_list),
 * printing out each lock in the dependency path will help on understanding how
 * the deadlock could happen. Here are some details about dependency path
 * printing:
 *
 * 1) A lock_list can be either forwards or backwards for a lock dependency,
 *  for a lock dependency A -> B, there are two lock_lists:
 *
 *  a) lock_list in the ->locks_after list of A, whose ->class is B and
 *  ->links_to is A. In this case, we can say the lock_list is
 *  "A -> B" (forwards case).
 *
 *  b) lock_list in the ->locks_before list of B, whose ->class is A
 *  and ->links_to is B. In this case, we can say the lock_list is
 *  "B <- A" (bacwards case).
 *
 *  The ->trace of both a) and b) point to the call trace where B was
 *  acquired with A held.
 *
 * 2) A "helper" lock_list is introduced during BFS, this lock_list doesn't
 *  represent a certain lock dependency, it only provides an initial entry
 *  for BFS. For example, BFS may introduce a "helper" lock_list whose
 *  ->class is A, as a result BFS will search all dependencies starting with
 *  A, e.g. A -> B or A -> C.
 *
 *  The notation of a forwards helper lock_list is like "-> A", which means
 *  we should search the forwards dependencies starting with "A", e.g A -> B
 *  or A -> C.
 *
 *  The notation of a bacwards helper lock_list is like "<- B", which means
 *  we should search the backwards dependencies ending with "B", e.g.
 *  B <- A or B <- C.
 */


/*
 * printk the shortest lock dependencies from @root to @leaf in reverse order.
 *
 * We have a lock dependency path as follow:
 *
 *    @root                                                                 @leaf
 *      |                                                                     |
 *      V                                                                     V
 *           ->parent                                   ->parent
 * | lock_list | <--------- | lock_list | ... | lock_list  | <--------- | lock_list |
 * |    -> L1  |            | L1 -> L2  | ... |Ln-2 -> Ln-1|            | Ln-1 -> Ln|
 *
 * , so it's natural that we start from @leaf and print every ->class and
 * ->trace until we reach the @root.
 */

static void __used
print_shortest_lock_dependencies(struct lock_list *leaf,
     struct lock_list *root)
{
 struct lock_list *entry = leaf;
 int depth;

 /*compute depth from generated tree by BFS*/
 depth = get_lock_depth(leaf);

 do {
  print_lock_class_header(entry->class, depth);
  printk("%*s ... acquired at:\n", depth, "");
  print_lock_trace(entry->trace, 2);
  printk("\n");

  if (depth == 0 && (entry != root)) {
   printk("lockdep:%s bad path found in chain graph\n", __func__);
   break;
  }

  entry = get_lock_parent(entry);
  depth--;
 } while (entry && (depth >= 0));
}

/*
 * printk the shortest lock dependencies from @leaf to @root.
 *
 * We have a lock dependency path (from a backwards search) as follow:
 *
 *    @leaf                                                                 @root
 *      |                                                                     |
 *      V                                                                     V
 *           ->parent                                   ->parent
 * | lock_list | ---------> | lock_list | ... | lock_list  | ---------> | lock_list |
 * | L2 <- L1  |            | L3 <- L2  | ... | Ln <- Ln-1 |            |    <- Ln  |
 *
 * , so when we iterate from @leaf to @root, we actually print the lock
 * dependency path L1 -> L2 -> .. -> Ln in the non-reverse order.
 *
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=96 G=94

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