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


Quelle  hw_breakpoint_test.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * KUnit test for hw_breakpoint constraints accounting logic.
 *
 * Copyright (C) 2022, Google LLC.
 */


#include <kunit/test.h>
#include <linux/cpumask.h>
#include <linux/hw_breakpoint.h>
#include <linux/kthread.h>
#include <linux/perf_event.h>
#include <asm/hw_breakpoint.h>

#define TEST_REQUIRES_BP_SLOTS(test, slots)      \
 do {          \
  if ((slots) > get_test_bp_slots()) {     \
   kunit_skip((test), "Requires breakpoint slots: %d > %d", slots, \
       get_test_bp_slots());    \
  }         \
 } while (0)

#define TEST_EXPECT_NOSPC(expr) KUNIT_EXPECT_EQ(test, -ENOSPC, PTR_ERR(expr))

#define MAX_TEST_BREAKPOINTS 512

static char break_vars[MAX_TEST_BREAKPOINTS];
static struct perf_event *test_bps[MAX_TEST_BREAKPOINTS];
static struct task_struct *__other_task;

static struct perf_event *register_test_bp(int cpu, struct task_struct *tsk, int idx)
{
 struct perf_event_attr attr = {};

 if (WARN_ON(idx < 0 || idx >= MAX_TEST_BREAKPOINTS))
  return NULL;

 hw_breakpoint_init(&attr);
 attr.bp_addr = (unsigned long)&break_vars[idx];
 attr.bp_len = HW_BREAKPOINT_LEN_1;
 attr.bp_type = HW_BREAKPOINT_RW;
 return perf_event_create_kernel_counter(&attr, cpu, tsk, NULL, NULL);
}

static void unregister_test_bp(struct perf_event **bp)
{
 if (WARN_ON(IS_ERR(*bp)))
  return;
 if (WARN_ON(!*bp))
  return;
 unregister_hw_breakpoint(*bp);
 *bp = NULL;
}

static int get_test_bp_slots(void)
{
 static int slots;

 if (!slots)
  slots = hw_breakpoint_slots(TYPE_DATA);

 return slots;
}

static void fill_one_bp_slot(struct kunit *test, int *id, int cpu, struct task_struct *tsk)
{
 struct perf_event *bp = register_test_bp(cpu, tsk, *id);

 KUNIT_ASSERT_NOT_NULL(test, bp);
 KUNIT_ASSERT_FALSE(test, IS_ERR(bp));
 KUNIT_ASSERT_NULL(test, test_bps[*id]);
 test_bps[(*id)++] = bp;
}

/*
 * Fills up the given @cpu/@tsk with breakpoints, only leaving @skip slots free.
 *
 * Returns true if this can be called again, continuing at @id.
 */

static bool fill_bp_slots(struct kunit *test, int *id, int cpu, struct task_struct *tsk, int skip)
{
 for (int i = 0; i < get_test_bp_slots() - skip; ++i)
  fill_one_bp_slot(test, id, cpu, tsk);

 return *id + get_test_bp_slots() <= MAX_TEST_BREAKPOINTS;
}

static int dummy_kthread(void *arg)
{
 return 0;
}

static struct task_struct *get_other_task(struct kunit *test)
{
 struct task_struct *tsk;

 if (__other_task)
  return __other_task;

 tsk = kthread_create(dummy_kthread, NULL, "hw_breakpoint_dummy_task");
 KUNIT_ASSERT_FALSE(test, IS_ERR(tsk));
 __other_task = tsk;
 return __other_task;
}

static int get_test_cpu(int num)
{
 int cpu;

 WARN_ON(num < 0);

 for_each_online_cpu(cpu) {
  if (num-- <= 0)
   break;
 }

 return cpu;
}

/* ===== Test cases ===== */

static void test_one_cpu(struct kunit *test)
{
 int idx = 0;

 fill_bp_slots(test, &idx, get_test_cpu(0), NULL, 0);
 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
}

static void test_many_cpus(struct kunit *test)
{
 int idx = 0;
 int cpu;

 /* Test that CPUs are independent. */
 for_each_online_cpu(cpu) {
  bool do_continue = fill_bp_slots(test, &idx, cpu, NULL, 0);

  TEST_EXPECT_NOSPC(register_test_bp(cpu, NULL, idx));
  if (!do_continue)
   break;
 }
}

static void test_one_task_on_all_cpus(struct kunit *test)
{
 int idx = 0;

 fill_bp_slots(test, &idx, -1, current, 0);
 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
 /* Remove one and adding back CPU-target should work. */
 unregister_test_bp(&test_bps[0]);
 fill_one_bp_slot(test, &idx, get_test_cpu(0), NULL);
}

static void test_two_tasks_on_all_cpus(struct kunit *test)
{
 int idx = 0;

 /* Test that tasks are independent. */
 fill_bp_slots(test, &idx, -1, current, 0);
 fill_bp_slots(test, &idx, -1, get_other_task(test), 0);

 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(-1, get_other_task(test), idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), get_other_task(test), idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
 /* Remove one from first task and adding back CPU-target should not work. */
 unregister_test_bp(&test_bps[0]);
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
}

static void test_one_task_on_one_cpu(struct kunit *test)
{
 int idx = 0;

 fill_bp_slots(test, &idx, get_test_cpu(0), current, 0);
 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
 /*
 * Remove one and adding back CPU-target should work; this case is
 * special vs. above because the task's constraints are CPU-dependent.
 */

 unregister_test_bp(&test_bps[0]);
 fill_one_bp_slot(test, &idx, get_test_cpu(0), NULL);
}

static void test_one_task_mixed(struct kunit *test)
{
 int idx = 0;

 TEST_REQUIRES_BP_SLOTS(test, 3);

 fill_one_bp_slot(test, &idx, get_test_cpu(0), current);
 fill_bp_slots(test, &idx, -1, current, 1);
 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));

 /* Transition from CPU-dependent pinned count to CPU-independent. */
 unregister_test_bp(&test_bps[0]);
 unregister_test_bp(&test_bps[1]);
 fill_one_bp_slot(test, &idx, get_test_cpu(0), NULL);
 fill_one_bp_slot(test, &idx, get_test_cpu(0), NULL);
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
}

static void test_two_tasks_on_one_cpu(struct kunit *test)
{
 int idx = 0;

 fill_bp_slots(test, &idx, get_test_cpu(0), current, 0);
 fill_bp_slots(test, &idx, get_test_cpu(0), get_other_task(test), 0);

 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(-1, get_other_task(test), idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), get_other_task(test), idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
 /* Can still create breakpoints on some other CPU. */
 fill_bp_slots(test, &idx, get_test_cpu(1), NULL, 0);
}

static void test_two_tasks_on_one_all_cpus(struct kunit *test)
{
 int idx = 0;

 fill_bp_slots(test, &idx, get_test_cpu(0), current, 0);
 fill_bp_slots(test, &idx, -1, get_other_task(test), 0);

 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(-1, get_other_task(test), idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), get_other_task(test), idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
 /* Cannot create breakpoints on some other CPU either. */
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(1), NULL, idx));
}

static void test_task_on_all_and_one_cpu(struct kunit *test)
{
 int tsk_on_cpu_idx, cpu_idx;
 int idx = 0;

 TEST_REQUIRES_BP_SLOTS(test, 3);

 fill_bp_slots(test, &idx, -1, current, 2);
 /* Transitioning from only all CPU breakpoints to mixed. */
 tsk_on_cpu_idx = idx;
 fill_one_bp_slot(test, &idx, get_test_cpu(0), current);
 fill_one_bp_slot(test, &idx, -1, current);

 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));

 /* We should still be able to use up another CPU's slots. */
 cpu_idx = idx;
 fill_one_bp_slot(test, &idx, get_test_cpu(1), NULL);
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(1), NULL, idx));

 /* Transitioning back to task target on all CPUs. */
 unregister_test_bp(&test_bps[tsk_on_cpu_idx]);
 /* Still have a CPU target breakpoint in get_test_cpu(1). */
 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 /* Remove it and try again. */
 unregister_test_bp(&test_bps[cpu_idx]);
 fill_one_bp_slot(test, &idx, -1, current);

 TEST_EXPECT_NOSPC(register_test_bp(-1, current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), current, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(0), NULL, idx));
 TEST_EXPECT_NOSPC(register_test_bp(get_test_cpu(1), NULL, idx));
}

static struct kunit_case hw_breakpoint_test_cases[] = {
 KUNIT_CASE(test_one_cpu),
 KUNIT_CASE(test_many_cpus),
 KUNIT_CASE(test_one_task_on_all_cpus),
 KUNIT_CASE(test_two_tasks_on_all_cpus),
 KUNIT_CASE(test_one_task_on_one_cpu),
 KUNIT_CASE(test_one_task_mixed),
 KUNIT_CASE(test_two_tasks_on_one_cpu),
 KUNIT_CASE(test_two_tasks_on_one_all_cpus),
 KUNIT_CASE(test_task_on_all_and_one_cpu),
 {},
};

static int test_init(struct kunit *test)
{
 /* Most test cases want 2 distinct CPUs. */
 if (num_online_cpus() < 2)
  kunit_skip(test, "not enough cpus");

 /* Want the system to not use breakpoints elsewhere. */
 if (hw_breakpoint_is_used())
  kunit_skip(test, "hw breakpoint already in use");

 return 0;
}

static void test_exit(struct kunit *test)
{
 for (int i = 0; i < MAX_TEST_BREAKPOINTS; ++i) {
  if (test_bps[i])
   unregister_test_bp(&test_bps[i]);
 }

 if (__other_task) {
  kthread_stop(__other_task);
  __other_task = NULL;
 }

 /* Verify that internal state agrees that no breakpoints are in use. */
 KUNIT_EXPECT_FALSE(test, hw_breakpoint_is_used());
}

static struct kunit_suite hw_breakpoint_test_suite = {
 .name = "hw_breakpoint",
 .test_cases = hw_breakpoint_test_cases,
 .init = test_init,
 .exit = test_exit,
};

kunit_test_suites(&hw_breakpoint_test_suite);

MODULE_AUTHOR("Marco Elver ");

Messung V0.5
C=95 H=93 G=93

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