Add lock-based readers and corresponding writers
diff --git a/rcuhashbash.c b/rcuhashbash.c
index 0ac8b1e..1532ece 100644
--- a/rcuhashbash.c
+++ b/rcuhashbash.c
@@ -51,6 +51,10 @@
 
 struct rcuhashbash_ops {
 	void (*init_bucket)(struct rcuhashbash_bucket *);
+	int (*reader_thread)(void *);
+	void (*read_lock_bucket)(struct rcuhashbash_bucket *);
+	void (*read_unlock_bucket)(struct rcuhashbash_bucket *);
+	int (*writer_thread)(void *);
 	void (*write_lock_buckets)(struct rcuhashbash_bucket *, struct rcuhashbash_bucket *);
 	void (*write_unlock_buckets)(struct rcuhashbash_bucket *, struct rcuhashbash_bucket *);
 	int max_writers;
@@ -118,7 +122,7 @@
 	return swahw32(rrsp->rrs_state);
 }
 
-static int rcuhashbash_reader(void *arg)
+static int rcuhashbash_reader_rcu(void *arg)
 {
 	struct reader_stats *stats = arg;
 	DEFINE_RCU_RANDOM(rand);
@@ -148,6 +152,39 @@
 	return 0;
 }
 
+static int rcuhashbash_reader_lock(void *arg)
+{
+	struct reader_stats *stats = arg;
+	DEFINE_RCU_RANDOM(rand);
+
+	set_user_nice(current, 19);
+
+	do {
+		struct rcuhashbash_entry *entry;
+		struct hlist_node *node;
+		u32 value, bucket;
+
+		cond_resched();
+
+		value = rcu_random(&rand) % (entries * 2);
+		bucket = value % buckets;
+
+		if (ops->read_lock_bucket)
+			ops->read_lock_bucket(&hash_table[bucket]);
+		hlist_for_each_entry(entry, node, &hash_table[value % buckets].head, node)
+			if (entry->value == value)
+				break;
+		if (node)
+			stats->hits++;
+		else
+			stats->misses++;
+		if (ops->read_unlock_bucket)
+			ops->read_unlock_bucket(&hash_table[bucket]);
+	} while (!kthread_should_stop());
+
+	return 0;
+}
+
 static void rcuhashbash_entry_cb(struct rcu_head *rcu_head)
 {
 	struct rcuhashbash_entry *entry;
@@ -155,7 +192,7 @@
 	kmem_cache_free(entry_cache, entry);
 }
 
-static int rcuhashbash_writer(void *arg)
+static int rcuhashbash_writer_rcu(void *arg)
 {
 	int err = 0;
 	struct writer_stats *stats = arg;
@@ -265,6 +302,82 @@
 	return err;
 }
 
+static int rcuhashbash_writer_lock(void *arg)
+{
+	struct writer_stats *stats = arg;
+	DEFINE_RCU_RANDOM(rand);
+
+	set_user_nice(current, 19);
+
+	do {
+		u32 src_value, src_bucket;
+		u32 dst_value, dst_bucket;
+		struct rcuhashbash_entry *entry = NULL;
+		struct hlist_node *node;
+		struct rcuhashbash_entry *src_entry = NULL;
+		bool same_bucket;
+		bool dest_in_use = false;
+
+		cond_resched();
+
+		src_value = rcu_random(&rand) % (entries * 2);
+		src_bucket = src_value % buckets;
+		dst_value = rcu_random(&rand) % (entries * 2);
+		dst_bucket = dst_value % buckets;
+		same_bucket = src_bucket == dst_bucket;
+
+		if (ops->write_lock_buckets)
+			ops->write_lock_buckets(&hash_table[src_bucket],
+			                        &hash_table[dst_bucket]);
+
+		/* Find src_entry. */
+		hlist_for_each_entry(entry, node, &hash_table[src_bucket].head, node) {
+			if (entry->value == src_value)
+				src_entry = entry;
+			if (same_bucket && entry->value == dst_value)
+				dest_in_use = true;
+		}
+		if (!src_entry) {
+			stats->misses++;
+			goto unlock_and_loop;
+		}
+		if (dest_in_use) {
+			stats->dests_in_use++;
+			goto unlock_and_loop;
+		}
+
+		if (same_bucket) {
+			src_entry->value = dst_value;
+			stats->moves++;
+			goto unlock_and_loop;
+		}
+
+		/* Check for existing destination. */
+		hlist_for_each_entry(entry, node, &hash_table[dst_bucket].head, node)
+			if (entry->value == dst_value) {
+				dest_in_use = true;
+				break;
+			}
+		if (dest_in_use) {
+			stats->dests_in_use++;
+			goto unlock_and_loop;
+		}
+
+		hlist_del(&src_entry->node);
+		src_entry->value = dst_value;
+		hlist_add_head(&src_entry->node, &hash_table[dst_bucket].head);
+
+		stats->moves++;
+
+unlock_and_loop:
+		if (ops->write_unlock_buckets)
+			ops->write_unlock_buckets(&hash_table[src_bucket],
+			                          &hash_table[dst_bucket]);
+	} while (!kthread_should_stop());
+
+	return 0;
+}
+
 static void spinlock_init_bucket(struct rcuhashbash_bucket *bucket)
 {
 	spin_lock_init(&bucket->spinlock);
@@ -280,6 +393,66 @@
 	mutex_init(&bucket->mutex);
 }
 
+static void spinlock_read_lock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	spin_lock(&bucket->spinlock);
+}
+
+static void rwlock_read_lock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	read_lock(&bucket->rwlock);
+}
+
+static void mutex_read_lock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	mutex_lock(&bucket->mutex);
+}
+
+static void table_spinlock_read_lock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	spin_lock(&table_spinlock);
+}
+
+static void table_rwlock_read_lock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	read_lock(&table_rwlock);
+}
+
+static void table_mutex_read_lock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	mutex_lock(&table_mutex);
+}
+
+static void spinlock_read_unlock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	spin_unlock(&bucket->spinlock);
+}
+
+static void rwlock_read_unlock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	read_unlock(&bucket->rwlock);
+}
+
+static void mutex_read_unlock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	mutex_unlock(&bucket->mutex);
+}
+
+static void table_spinlock_read_unlock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	spin_unlock(&table_spinlock);
+}
+
+static void table_rwlock_read_unlock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	read_unlock(&table_rwlock);
+}
+
+static void table_mutex_read_unlock_bucket(struct rcuhashbash_bucket *bucket)
+{
+	mutex_unlock(&table_mutex);
+}
+
 static void spinlock_write_lock_buckets(struct rcuhashbash_bucket *b1,
                                         struct rcuhashbash_bucket *b2)
 {
@@ -386,12 +559,16 @@
 	{
 		.reader_type = "rcu",
 		.writer_type = "single",
+		.reader_thread = rcuhashbash_reader_rcu,
+		.writer_thread = rcuhashbash_writer_rcu,
 		.max_writers = 1,
 	},
 	{
 		.reader_type = "rcu",
 		.writer_type = "spinlock",
 		.init_bucket = spinlock_init_bucket,
+		.reader_thread = rcuhashbash_reader_rcu,
+		.writer_thread = rcuhashbash_writer_rcu,
 		.write_lock_buckets = spinlock_write_lock_buckets,
 		.write_unlock_buckets = spinlock_write_unlock_buckets,
 	},
@@ -399,6 +576,8 @@
 		.reader_type = "rcu",
 		.writer_type = "rwlock",
 		.init_bucket = rwlock_init_bucket,
+		.reader_thread = rcuhashbash_reader_rcu,
+		.writer_thread = rcuhashbash_writer_rcu,
 		.write_lock_buckets = rwlock_write_lock_buckets,
 		.write_unlock_buckets = rwlock_write_unlock_buckets,
 	},
@@ -406,24 +585,95 @@
 		.reader_type = "rcu",
 		.writer_type = "mutex",
 		.init_bucket = mutex_init_bucket,
+		.reader_thread = rcuhashbash_reader_rcu,
+		.writer_thread = rcuhashbash_writer_rcu,
 		.write_lock_buckets = mutex_write_lock_buckets,
 		.write_unlock_buckets = mutex_write_unlock_buckets,
 	},
 	{
 		.reader_type = "rcu",
 		.writer_type = "table_spinlock",
+		.reader_thread = rcuhashbash_reader_rcu,
+		.writer_thread = rcuhashbash_writer_rcu,
 		.write_lock_buckets = table_spinlock_write_lock_buckets,
 		.write_unlock_buckets = table_spinlock_write_unlock_buckets,
 	},
 	{
 		.reader_type = "rcu",
 		.writer_type = "table_rwlock",
+		.reader_thread = rcuhashbash_reader_rcu,
+		.writer_thread = rcuhashbash_writer_rcu,
 		.write_lock_buckets = table_rwlock_write_lock_buckets,
 		.write_unlock_buckets = table_rwlock_write_unlock_buckets,
 	},
 	{
 		.reader_type = "rcu",
 		.writer_type = "table_mutex",
+		.reader_thread = rcuhashbash_reader_rcu,
+		.writer_thread = rcuhashbash_writer_rcu,
+		.write_lock_buckets = table_mutex_write_lock_buckets,
+		.write_unlock_buckets = table_mutex_write_unlock_buckets,
+	},
+	{
+		.reader_type = "spinlock",
+		.writer_type = "spinlock",
+		.init_bucket = spinlock_init_bucket,
+		.reader_thread = rcuhashbash_reader_lock,
+		.read_lock_bucket = spinlock_read_lock_bucket,
+		.read_unlock_bucket = spinlock_read_unlock_bucket,
+		.writer_thread = rcuhashbash_writer_lock,
+		.write_lock_buckets = spinlock_write_lock_buckets,
+		.write_unlock_buckets = spinlock_write_unlock_buckets,
+	},
+	{
+		.reader_type = "rwlock",
+		.writer_type = "rwlock",
+		.init_bucket = rwlock_init_bucket,
+		.reader_thread = rcuhashbash_reader_lock,
+		.read_lock_bucket = rwlock_read_lock_bucket,
+		.read_unlock_bucket = rwlock_read_unlock_bucket,
+		.writer_thread = rcuhashbash_writer_lock,
+		.write_lock_buckets = rwlock_write_lock_buckets,
+		.write_unlock_buckets = rwlock_write_unlock_buckets,
+	},
+	{
+		.reader_type = "mutex",
+		.writer_type = "mutex",
+		.init_bucket = mutex_init_bucket,
+		.reader_thread = rcuhashbash_reader_lock,
+		.read_lock_bucket = mutex_read_lock_bucket,
+		.read_unlock_bucket = mutex_read_unlock_bucket,
+		.writer_thread = rcuhashbash_writer_lock,
+		.write_lock_buckets = mutex_write_lock_buckets,
+		.write_unlock_buckets = mutex_write_unlock_buckets,
+	},
+	{
+		.reader_type = "table_spinlock",
+		.writer_type = "table_spinlock",
+		.reader_thread = rcuhashbash_reader_lock,
+		.read_lock_bucket = table_spinlock_read_lock_bucket,
+		.read_unlock_bucket = table_spinlock_read_unlock_bucket,
+		.writer_thread = rcuhashbash_writer_lock,
+		.write_lock_buckets = table_spinlock_write_lock_buckets,
+		.write_unlock_buckets = table_spinlock_write_unlock_buckets,
+	},
+	{
+		.reader_type = "table_rwlock",
+		.writer_type = "table_rwlock",
+		.reader_thread = rcuhashbash_reader_lock,
+		.read_lock_bucket = table_rwlock_read_lock_bucket,
+		.read_unlock_bucket = table_rwlock_read_unlock_bucket,
+		.writer_thread = rcuhashbash_writer_lock,
+		.write_lock_buckets = table_rwlock_write_lock_buckets,
+		.write_unlock_buckets = table_rwlock_write_unlock_buckets,
+	},
+	{
+		.reader_type = "table_mutex",
+		.writer_type = "table_mutex",
+		.reader_thread = rcuhashbash_reader_lock,
+		.read_lock_bucket = table_mutex_read_lock_bucket,
+		.read_unlock_bucket = table_mutex_read_unlock_bucket,
+		.writer_thread = rcuhashbash_writer_lock,
 		.write_lock_buckets = table_mutex_write_lock_buckets,
 		.write_unlock_buckets = table_mutex_write_unlock_buckets,
 	},
@@ -530,6 +780,10 @@
 		       reader_type, writer_type);
 		return -EINVAL;
 	}
+	if (!ops->reader_thread || !ops->writer_thread) {
+		printk(KERN_ALERT "rcuhashbash: Internal error: reader or writer thread NULL\n");
+		return -EINVAL;
+	}
 
 	if (readers < 0)
 		readers = num_online_cpus();
@@ -582,7 +836,7 @@
 
 	for (i = 0; i < readers; i++) {
 		struct task_struct *task;
-		task = kthread_run(rcuhashbash_reader, &reader_stats[i],
+		task = kthread_run(ops->reader_thread, &reader_stats[i],
 		                   "rcuhashbash_reader");
 		if (IS_ERR(task)) {
 			ret = PTR_ERR(task);
@@ -593,7 +847,7 @@
 
 	for (i = 0; i < writers; i++) {
 		struct task_struct *task;
-		task = kthread_run(rcuhashbash_writer, &writer_stats[i],
+		task = kthread_run(ops->writer_thread, &writer_stats[i],
 		                   "rcuhashbash_writer");
 		if (IS_ERR(task)) {
 			ret = PTR_ERR(task);