diff mbox

[5/5] reservation: Add lockdep annotation and selftests

Message ID 20120928124321.14366.35302.stgit@patser.local (mailing list archive)
State New, archived
Headers show

Commit Message

Maarten Lankhorst Sept. 28, 2012, 12:43 p.m. UTC
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>

---

The self-tests will fail if the commit "lockdep: Check if nested
lock is actually held" from linux tip core/locking is not applied.
---
 drivers/base/reservation.c  |   46 +++++-
 include/linux/reservation.h |   29 +++-
 lib/Kconfig.debug           |    1 
 lib/locking-selftest.c      |  353 +++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 403 insertions(+), 26 deletions(-)
diff mbox

Patch

diff --git a/drivers/base/reservation.c b/drivers/base/reservation.c
index 93e2d9f..b8d4f4d 100644
--- a/drivers/base/reservation.c
+++ b/drivers/base/reservation.c
@@ -41,6 +41,18 @@ 
 atomic64_t reservation_counter = ATOMIC64_INIT(1);
 EXPORT_SYMBOL(reservation_counter);
 
+const char reservation_object_name[] = "reservation_object";
+EXPORT_SYMBOL(reservation_object_name);
+
+const char reservation_ticket_name[] = "reservation_ticket";
+EXPORT_SYMBOL(reservation_ticket_name);
+
+struct lock_class_key reservation_object_class;
+EXPORT_SYMBOL(reservation_object_class);
+
+struct lock_class_key reservation_ticket_class;
+EXPORT_SYMBOL(reservation_ticket_class);
+
 int
 object_reserve(struct reservation_object *obj, bool intr, bool no_wait,
 	       reservation_ticket_t *ticket)
@@ -49,6 +61,10 @@  object_reserve(struct reservation_object *obj, bool intr, bool no_wait,
 	u64 sequence = ticket ? ticket->seqno : 1;
 	u64 oldseq;
 
+	if (!no_wait)
+		mutex_acquire_nest(&obj->dep_map, 0, 0,
+				   ticket ? &ticket->dep_map : NULL, _RET_IP_);
+
 	while (unlikely(oldseq = atomic64_cmpxchg(&obj->reserved, 0, sequence))) {
 
 		/**
@@ -58,14 +74,18 @@  object_reserve(struct reservation_object *obj, bool intr, bool no_wait,
 			/**
 			 * We've already reserved this one.
 			 */
-			if (unlikely(sequence == oldseq))
-				return -EDEADLK;
+			if (unlikely(sequence == oldseq)) {
+				ret = -EDEADLK;
+				goto fail;
+			}
 			/**
 			 * Already reserved by a thread that will not back
 			 * off for us. We need to back off.
 			 */
-			if (unlikely(sequence - oldseq < (1ULL << 63)))
-				return -EAGAIN;
+			if (unlikely(sequence - oldseq < (1ULL << 63))) {
+				ret = -EAGAIN;
+				goto fail;
+			}
 		}
 
 		if (no_wait)
@@ -74,9 +94,12 @@  object_reserve(struct reservation_object *obj, bool intr, bool no_wait,
 		ret = object_wait_unreserved(obj, intr);
 
 		if (unlikely(ret))
-			return ret;
+			goto fail;
 	}
 
+	if (no_wait)
+		mutex_acquire(&obj->dep_map, 0, 1, _RET_IP_);
+
 	/**
 	 * Wake up waiters that may need to recheck for deadlock,
 	 * if we decreased the sequence number.
@@ -84,6 +107,11 @@  object_reserve(struct reservation_object *obj, bool intr, bool no_wait,
 	wake_up_all(&obj->event_queue);
 
 	return 0;
+
+fail:
+	if (!no_wait)
+		mutex_release(&obj->dep_map, 1, _RET_IP_);
+	return ret;
 }
 EXPORT_SYMBOL(object_reserve);
 
@@ -105,6 +133,14 @@  void
 object_unreserve(struct reservation_object *obj,
 		 reservation_ticket_t *ticket)
 {
+	mutex_release(&obj->dep_map, 1, _RET_IP_);
+
+	if (!object_is_reserved(obj)) {
+#ifndef CONFIG_DEBUG_LOCKING_API_SELFTESTS
+		WARN_ON(1);
+#endif
+		return;
+	}
 	smp_mb();
 	atomic64_set(&obj->reserved, 0);
 	wake_up_all(&obj->event_queue);
diff --git a/include/linux/reservation.h b/include/linux/reservation.h
index 93280af..6fa0cdb 100644
--- a/include/linux/reservation.h
+++ b/include/linux/reservation.h
@@ -44,6 +44,10 @@ 
 #include <linux/fence.h>
 
 extern atomic64_t reservation_counter;
+extern const char reservation_object_name[];
+extern struct lock_class_key reservation_object_class;
+extern const char reservation_ticket_name[];
+extern struct lock_class_key reservation_ticket_class;
 
 struct reservation_object {
 	wait_queue_head_t event_queue;
@@ -53,10 +57,17 @@  struct reservation_object {
 	u32 fence_shared_count;
 	struct fence *fence_excl;
 	struct fence *fence_shared[BUF_MAX_SHARED_FENCE];
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	struct lockdep_map dep_map;
+#endif
 };
 
 typedef struct reservation_ticket {
 	u64 seqno;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	struct lockdep_map dep_map;
+#endif
 } reservation_ticket_t;
 
 /**
@@ -73,11 +84,13 @@  struct reservation_entry {
 	unsigned long obj_shared;
 };
 
-
 static inline void
 __reservation_object_init(struct reservation_object *obj)
 {
 	init_waitqueue_head(&obj->event_queue);
+
+	lockdep_init_map(&obj->dep_map, reservation_object_name,
+			 &reservation_object_class, 0);
 }
 
 static inline void
@@ -110,6 +123,16 @@  reservation_object_fini(struct reservation_object *obj)
 static inline void
 reservation_ticket_init(struct reservation_ticket *t)
 {
+#ifdef CONFIG_LOCKDEP
+	/*
+	 * Make sure we are not reinitializing a held ticket:
+	 */
+
+	debug_check_no_locks_freed((void *)t, sizeof(*t));
+#endif
+	lockdep_init_map(&t->dep_map, reservation_ticket_name,
+			 &reservation_ticket_class, 0);
+	mutex_acquire(&t->dep_map, 0, 0, _RET_IP_);
 	do {
 		t->seqno = atomic64_inc_return(&reservation_counter);
 	} while (unlikely(t->seqno < 2));
@@ -125,7 +148,9 @@  reservation_ticket_init(struct reservation_ticket *t)
  */
 static inline void
 reservation_ticket_fini(struct reservation_ticket *t)
-{ }
+{
+	mutex_release(&t->dep_map, 1, _RET_IP_);
+}
 
 /**
  * reservation_entry_init - initialize and append a reservation_entry
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2403a63..3211730 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -700,6 +700,7 @@  config DEBUG_ATOMIC_SLEEP
 config DEBUG_LOCKING_API_SELFTESTS
 	bool "Locking API boot-time self-tests"
 	depends on DEBUG_KERNEL
+	select CONFIG_DMA_SHARED_BUFFER
 	help
 	  Say Y here if you want the kernel to run a short self-test during
 	  bootup. The self-test checks whether common types of locking bugs
diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c
index 7aae0f2..fc4a45b 100644
--- a/lib/locking-selftest.c
+++ b/lib/locking-selftest.c
@@ -20,6 +20,7 @@ 
 #include <linux/interrupt.h>
 #include <linux/debug_locks.h>
 #include <linux/irqflags.h>
+#include <linux/reservation.h>
 
 /*
  * Change this to 1 if you want to see the failure printouts:
@@ -42,6 +43,7 @@  __setup("debug_locks_verbose=", setup_debug_locks_verbose);
 #define LOCKTYPE_RWLOCK	0x2
 #define LOCKTYPE_MUTEX	0x4
 #define LOCKTYPE_RWSEM	0x8
+#define LOCKTYPE_RESERVATION 0x10
 
 /*
  * Normal standalone locks, for the circular and irq-context
@@ -920,11 +922,17 @@  GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_soft)
 static void reset_locks(void)
 {
 	local_irq_disable();
+	lockdep_free_key_range(&reservation_object_class, 1);
+	lockdep_free_key_range(&reservation_ticket_class, 1);
+
 	I1(A); I1(B); I1(C); I1(D);
 	I1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2);
 	lockdep_reset();
 	I2(A); I2(B); I2(C); I2(D);
 	init_shared_classes();
+
+	memset(&reservation_object_class, 0, sizeof reservation_object_class);
+	memset(&reservation_ticket_class, 0, sizeof reservation_ticket_class);
 	local_irq_enable();
 }
 
@@ -938,7 +946,6 @@  static int unexpected_testcase_failures;
 static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask)
 {
 	unsigned long saved_preempt_count = preempt_count();
-	int expected_failure = 0;
 
 	WARN_ON(irqs_disabled());
 
@@ -946,26 +953,16 @@  static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask)
 	/*
 	 * Filter out expected failures:
 	 */
+	if (debug_locks != expected) {
 #ifndef CONFIG_PROVE_LOCKING
-	if ((lockclass_mask & LOCKTYPE_SPIN) && debug_locks != expected)
-		expected_failure = 1;
-	if ((lockclass_mask & LOCKTYPE_RWLOCK) && debug_locks != expected)
-		expected_failure = 1;
-	if ((lockclass_mask & LOCKTYPE_MUTEX) && debug_locks != expected)
-		expected_failure = 1;
-	if ((lockclass_mask & LOCKTYPE_RWSEM) && debug_locks != expected)
-		expected_failure = 1;
+		expected_testcase_failures++;
+		printk("failed|");
+#else
+		unexpected_testcase_failures++;
+		printk("FAILED|");
+
+		dump_stack();
 #endif
-	if (debug_locks != expected) {
-		if (expected_failure) {
-			expected_testcase_failures++;
-			printk("failed|");
-		} else {
-			unexpected_testcase_failures++;
-
-			printk("FAILED|");
-			dump_stack();
-		}
 	} else {
 		testcase_successes++;
 		printk("  ok  |");
@@ -1108,6 +1105,322 @@  static inline void print_testname(const char *testname)
 	DO_TESTCASE_6IRW(desc, name, 312);			\
 	DO_TESTCASE_6IRW(desc, name, 321);
 
+static void reservation_test_fail_reserve(void)
+{
+	struct reservation_ticket t;
+	struct reservation_object o;
+
+	reservation_object_init(&o);
+	reservation_ticket_init(&t);
+	t.seqno++;
+
+	object_reserve(&o, false, false, &t);
+	/* No lockdep test, pure API */
+	WARN_ON(object_reserve(&o, false, true, &t) != -EDEADLK);
+	t.seqno--;
+	WARN_ON(object_reserve(&o, false, true, &t) != -EBUSY);
+	t.seqno += 2;
+	WARN_ON(object_reserve(&o, false, true, &t) != -EAGAIN);
+	object_unreserve(&o, NULL);
+
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_two_tickets(void)
+{
+	struct reservation_ticket t, t2;
+
+	reservation_ticket_init(&t);
+	reservation_ticket_init(&t2);
+
+	reservation_ticket_fini(&t2);
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_ticket_unreserve_twice(void)
+{
+	struct reservation_ticket t;
+
+	reservation_ticket_init(&t);
+	reservation_ticket_fini(&t);
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_object_unreserve_twice(void)
+{
+	struct reservation_object o;
+
+	reservation_object_init(&o);
+	object_reserve(&o, false, false, NULL);
+	object_unreserve(&o, NULL);
+	object_unreserve(&o, NULL);
+}
+
+static void reservation_test_fence_nest_unreserved(void)
+{
+	struct reservation_object o;
+
+	reservation_object_init(&o);
+
+	spin_lock_nest_lock(&lock_A, &o);
+	spin_unlock(&lock_A);
+}
+
+static void reservation_test_ticket_block(void)
+{
+	struct reservation_ticket t;
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+	reservation_ticket_init(&t);
+
+	object_reserve(&o, false, false, &t);
+	object_reserve(&o2, false, false, NULL);
+	object_unreserve(&o2, NULL);
+	object_unreserve(&o, &t);
+
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_ticket_try(void)
+{
+	struct reservation_ticket t;
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+	reservation_ticket_init(&t);
+
+	object_reserve(&o, false, false, &t);
+	object_reserve(&o2, false, true, NULL);
+	object_unreserve(&o2, NULL);
+	object_unreserve(&o, &t);
+
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_ticket_ticket(void)
+{
+	struct reservation_ticket t;
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+	reservation_ticket_init(&t);
+
+	object_reserve(&o, false, false, &t);
+	object_reserve(&o2, false, false, &t);
+	object_unreserve(&o2, &t);
+	object_unreserve(&o, &t);
+
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_try_block(void)
+{
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+
+	object_reserve(&o, false, true, NULL);
+	object_reserve(&o2, false, false, NULL);
+	object_unreserve(&o2, NULL);
+	object_unreserve(&o, NULL);
+}
+
+static void reservation_test_try_try(void)
+{
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+
+	object_reserve(&o, false, true, NULL);
+	object_reserve(&o2, false, true, NULL);
+	object_unreserve(&o2, NULL);
+	object_unreserve(&o, NULL);
+}
+
+static void reservation_test_try_ticket(void)
+{
+	struct reservation_ticket t;
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+
+	object_reserve(&o, false, true, NULL);
+	reservation_ticket_init(&t);
+
+	object_reserve(&o2, false, false, &t);
+	object_unreserve(&o2, &t);
+	object_unreserve(&o, NULL);
+
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_block_block(void)
+{
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+
+	object_reserve(&o, false, false, NULL);
+	object_reserve(&o2, false, false, NULL);
+	object_unreserve(&o2, NULL);
+	object_unreserve(&o, NULL);
+}
+
+static void reservation_test_block_try(void)
+{
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+
+	object_reserve(&o, false, false, NULL);
+	object_reserve(&o2, false, true, NULL);
+	object_unreserve(&o2, NULL);
+	object_unreserve(&o, NULL);
+}
+
+static void reservation_test_block_ticket(void)
+{
+	struct reservation_ticket t;
+	struct reservation_object o, o2;
+
+	reservation_object_init(&o);
+	reservation_object_init(&o2);
+
+	object_reserve(&o, false, false, NULL);
+	reservation_ticket_init(&t);
+
+	object_reserve(&o2, false, false, &t);
+	object_unreserve(&o2, &t);
+	object_unreserve(&o, NULL);
+
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_test_fence_block(void)
+{
+	struct reservation_object o;
+
+	reservation_object_init(&o);
+	spin_lock(&lock_A);
+	spin_unlock(&lock_A);
+
+	object_reserve(&o, false, false, NULL);
+	spin_lock(&lock_A);
+	spin_unlock(&lock_A);
+	object_unreserve(&o, NULL);
+
+	spin_lock(&lock_A);
+	object_reserve(&o, false, false, NULL);
+	object_unreserve(&o, NULL);
+	spin_unlock(&lock_A);
+}
+
+static void reservation_test_fence_try(void)
+{
+	struct reservation_object o;
+
+	reservation_object_init(&o);
+	spin_lock(&lock_A);
+	spin_unlock(&lock_A);
+
+	object_reserve(&o, false, true, NULL);
+	spin_lock(&lock_A);
+	spin_unlock(&lock_A);
+	object_unreserve(&o, NULL);
+
+	spin_lock(&lock_A);
+	object_reserve(&o, false, true, NULL);
+	object_unreserve(&o, NULL);
+	spin_unlock(&lock_A);
+}
+
+static void reservation_test_fence_ticket(void)
+{
+	struct reservation_ticket t;
+	struct reservation_object o;
+
+	reservation_object_init(&o);
+	spin_lock(&lock_A);
+	spin_unlock(&lock_A);
+
+	reservation_ticket_init(&t);
+
+	object_reserve(&o, false, false, &t);
+	spin_lock(&lock_A);
+	spin_unlock(&lock_A);
+	object_unreserve(&o, &t);
+
+	spin_lock(&lock_A);
+	object_reserve(&o, false, false, &t);
+	object_unreserve(&o, &t);
+	spin_unlock(&lock_A);
+
+	reservation_ticket_fini(&t);
+}
+
+static void reservation_tests(void)
+{
+	printk("  --------------------------------------------------------------------------\n");
+	printk("  | Reservation tests |\n");
+	printk("  ---------------------\n");
+
+	print_testname("reservation api failures");
+	dotest(reservation_test_fail_reserve, SUCCESS, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	print_testname("reserving two tickets");
+	dotest(reservation_test_two_tickets, FAILURE, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	print_testname("unreserve ticket twice");
+	dotest(reservation_test_ticket_unreserve_twice, FAILURE, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	print_testname("unreserve object twice");
+	dotest(reservation_test_object_unreserve_twice, FAILURE, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	print_testname("spinlock nest unreserved");
+	dotest(reservation_test_fence_nest_unreserved, FAILURE, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	printk("  -----------------------------------------------------\n");
+	printk("                                 |block | try  |ticket|\n");
+	printk("  -----------------------------------------------------\n");
+
+	print_testname("ticket");
+	dotest(reservation_test_ticket_block, FAILURE, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_ticket_try, SUCCESS, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_ticket_ticket, SUCCESS, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	print_testname("try");
+	dotest(reservation_test_try_block, FAILURE, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_try_try, SUCCESS, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_try_ticket, FAILURE, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	print_testname("block");
+	dotest(reservation_test_block_block, FAILURE, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_block_try, SUCCESS, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_block_ticket, FAILURE, LOCKTYPE_RESERVATION);
+	printk("\n");
+
+	print_testname("spinlock");
+	dotest(reservation_test_fence_block, FAILURE, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_fence_try, SUCCESS, LOCKTYPE_RESERVATION);
+	dotest(reservation_test_fence_ticket, FAILURE, LOCKTYPE_RESERVATION);
+	printk("\n");
+}
 
 void locking_selftest(void)
 {
@@ -1188,6 +1501,8 @@  void locking_selftest(void)
 	DO_TESTCASE_6x2("irq read-recursion", irq_read_recursion);
 //	DO_TESTCASE_6x2B("irq read-recursion #2", irq_read_recursion2);
 
+	reservation_tests();
+
 	if (unexpected_testcase_failures) {
 		printk("-----------------------------------------------------------------\n");
 		debug_locks = 0;