[PATCH][RFC] random: show /dev/random statistics per interface, kernel 2.6.26.1, v2

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Stanichenko Marat <mstanichenko@xxxxxxxxx>

Add the interface that shows the amount of entropy generated via the interfaces and also the amount consumed. This patch adds two files in proc. The first of them shows the entropy generated per interface, the second one - the amount consumed from blocking (/dev/random) and nonblocking (/dev/urandom) pools.

This patch is an attempt to realize "/dev/random statistics" project of kernel newbies community.
http://kernelnewbies.org/KernelProjects

Signed-off-by: Stanichenko Marat <mstanichenko@xxxxxxxxx>
---
 block/genhd.c          |    2 
 drivers/char/random.c  |  221 +++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/input/input.c  |    1 
 include/linux/genhd.h  |    3 
 include/linux/random.h |    1 
 5 files changed, 221 insertions(+), 7 deletions(-)
diff -upr -X linux-2.6.26.1.rand/Documentation/dontdiff linux-2.6.26.1/block/genhd.c linux-2.6.26.1.rand/block/genhd.c
--- linux-2.6.26.1/block/genhd.c	2008-08-02 02:58:24.000000000 +0400
+++ linux-2.6.26.1.rand/block/genhd.c	2008-08-23 15:00:21.000000000 +0400
@@ -505,7 +505,7 @@ static void disk_release(struct device *
 {
 	struct gendisk *disk = dev_to_disk(dev);
 
-	kfree(disk->random);
+	rand_uninitialize_disk(disk);
 	kfree(disk->part);
 	free_disk_stats(disk);
 	kfree(disk);
diff -upr -X linux-2.6.26.1.rand/Documentation/dontdiff linux-2.6.26.1/drivers/char/random.c linux-2.6.26.1.rand/drivers/char/random.c
--- linux-2.6.26.1/drivers/char/random.c	2008-08-02 02:58:24.000000000 +0400
+++ linux-2.6.26.1.rand/drivers/char/random.c	2008-08-24 02:14:08.000000000 +0400
@@ -239,6 +239,9 @@
 #include <linux/spinlock.h>
 #include <linux/percpu.h>
 #include <linux/cryptohash.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/list.h>
 
 #include <asm/processor.h>
 #include <asm/uaccess.h>
@@ -554,11 +557,25 @@ struct timer_rand_state {
 	cycles_t last_time;
 	long last_delta, last_delta2;
 	unsigned dont_count_entropy:1;
+	unsigned long *nbits;
 };
 
 static struct timer_rand_state input_timer_state;
 static struct timer_rand_state *irq_timer_state[NR_IRQS];
 
+struct ioctl_rand_stat {
+	unsigned nbits;
+	spinlock_t lock;
+};
+
+static struct ioctl_rand_stat ent_ioctl_produce = {
+	.nbits = 0,
+	.lock = __SPIN_LOCK_UNLOCKED(&ent_ioctl_produce.lock),
+};
+
+static unsigned ent_block_consume = 0;
+static unsigned ent_noblock_consume = 0;
+
 /*
  * This function adds entropy to the entropy "pool" by using timing
  * delays.  It uses the timer_rand_state structure to make an estimate
@@ -577,6 +594,7 @@ static void add_timer_randomness(struct 
 		unsigned num;
 	} sample;
 	long delta, delta2, delta3;
+	int nbits, flags;
 
 	preempt_disable();
 	/* if over the trickle threshold, use only 1 in 4096 samples */
@@ -621,8 +639,11 @@ static void add_timer_randomness(struct 
 		 * Round down by 1 bit on general principles,
 		 * and limit entropy entimate to 12 bits.
 		 */
-		credit_entropy_bits(&input_pool,
-				    min_t(int, fls(delta>>1), 11));
+		nbits = min_t(int, fls(delta>>1), 11);
+		local_irq_save(flags);
+		*per_cpu_ptr(state->nbits, smp_processor_id()) += nbits;
+		local_irq_restore(flags);
+		credit_entropy_bits(&input_pool,nbits);
 	}
 out:
 	preempt_enable();
@@ -654,6 +675,18 @@ void add_interrupt_randomness(int irq)
 }
 
 #ifdef CONFIG_BLOCK
+struct rand_disk_list {
+	struct list_head head_disk_list;
+	rwlock_t lock;
+	int num_disk_list;
+};
+
+static struct rand_disk_list rand_disk_head = {
+	.head_disk_list = LIST_HEAD_INIT(rand_disk_head.head_disk_list),
+	.num_disk_list = 0,
+	.lock = __RW_LOCK_UNLOCKED(rand_disk_head.lock),
+};
+
 void add_disk_randomness(struct gendisk *disk)
 {
 	if (!disk || !disk->random)
@@ -741,10 +774,18 @@ static size_t account(struct entropy_sto
 		if (r->limit && nbytes + reserved >= r->entropy_count / 8)
 			nbytes = r->entropy_count/8 - reserved;
 
-		if (r->entropy_count / 8 >= nbytes + reserved)
+		if (r->entropy_count / 8 >= nbytes + reserved) {
 			r->entropy_count -= nbytes*8;
-		else
+			if (r == &blocking_pool)
+				ent_block_consume += nbytes*8;
+			if (r == &nonblocking_pool)
+				ent_noblock_consume += nbytes*8;
+		}
+		else {
+			if (r == &nonblocking_pool)
+				ent_noblock_consume += (r->entropy_count - reserved);
 			r->entropy_count = reserved;
+		}
 
 		if (r->entropy_count < random_write_wakeup_thresh) {
 			wake_up_interruptible(&random_write_wait);
@@ -897,11 +938,144 @@ static void init_std_data(struct entropy
 	mix_pool_bytes(r, utsname(), sizeof(*(utsname())));
 }
 
+#ifdef CONFIG_PROC_FS
+int show_entropy_produced(struct seq_file *p, void *v)
+{
+	int cpu, i = *(loff_t *) v;
+	struct timer_rand_state *state;
+	static struct list_head *list_ptr;
+	struct gendisk *disk_ptr;
+	unsigned long nbits = 0;
+
+	if (i == 0)
+		seq_printf(p, "Entropy produced v.0.1\n");
+
+	if ((i < NR_IRQS) && (irq_timer_state[i])) {
+		state = irq_timer_state[i];
+		seq_printf(p, "%5d: ", i);
+	} else if (i == NR_IRQS) {
+		state = &input_timer_state;
+		seq_printf(p, "  inp: ");
+	} else if ((i > NR_IRQS) &&
+		(i <= NR_IRQS + rand_disk_head.num_disk_list)) {
+		if (i == (NR_IRQS + 1))
+			list_ptr = &rand_disk_head.head_disk_list;
+		list_ptr = list_ptr->next;
+		disk_ptr = list_entry(list_ptr, struct gendisk, random_list);
+		state = disk_ptr->random;
+		seq_printf(p, "%5s: ", disk_ptr->disk_name);
+	} else
+		goto check_ioctl;
+	for_each_online_cpu(cpu)
+		nbits += *per_cpu_ptr(state->nbits, cpu);
+	seq_printf(p, "%8lu\n", nbits);
+	return 0;
+
+check_ioctl:
+	if (i == NR_IRQS + rand_disk_head.num_disk_list + 1)
+		seq_printf(p, "ioctl: %8d\n", ent_ioctl_produce.nbits);
+	return 0;
+}
+
+int show_entropy_consumed(struct seq_file *p, void *v)
+{
+	int i = *(loff_t *) v;
+	if (i == 0) {
+		seq_printf(p, "Entropy consumed v.0.1\n");
+		seq_printf(p, "  block: %8d\n", ent_block_consume);
+	} else
+		seq_printf(p, "noblock: %8d\n", ent_noblock_consume);
+	return 0;
+}
+
+static void *prod_seq_start(struct seq_file *f, loff_t *pos)
+{
+	read_lock(&rand_disk_head.lock);
+	return (*pos <= NR_IRQS + rand_disk_head.num_disk_list + 1) ? pos : NULL;
+}
+
+static void *cons_seq_start(struct seq_file *f, loff_t *pos)
+{
+	read_lock(&rand_disk_head.lock);
+	return (*pos <= 1) ? pos : NULL;
+}
+
+static void *prod_seq_next(struct seq_file *f, void *v, loff_t *pos)
+{
+	(*pos)++;
+	if (*pos > NR_IRQS + rand_disk_head.num_disk_list + 1)
+		return NULL;
+	return pos;
+}
+
+static void *cons_seq_next(struct seq_file *f, void *v, loff_t *pos)
+{
+	(*pos)++;
+	if (*pos > 1)
+		return NULL;
+	return pos;
+}
+
+static void ent_seq_stop(struct seq_file *f, void *v)
+{
+	read_unlock(&rand_disk_head.lock);
+}
+
+static struct seq_operations prod_seq_ops = {
+	.start	= prod_seq_start,
+	.next	= prod_seq_next,
+	.stop	= ent_seq_stop,
+	.show	= show_entropy_produced,
+};
+
+static struct seq_operations cons_seq_ops = {
+	.start	= cons_seq_start,
+	.next	= cons_seq_next,
+	.stop	= ent_seq_stop,
+	.show	= show_entropy_consumed,
+};
+
+static int prod_open(struct inode *inode, struct file *filp)
+{
+	return seq_open(filp, &prod_seq_ops);
+}
+
+static int cons_open(struct inode *inode, struct file *filp)
+{
+	return seq_open(filp, &cons_seq_ops);
+}
+
+static struct file_operations prod_file_ops = {
+	.open		= prod_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct file_operations cons_file_ops = {
+	.open		= cons_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+#endif
+
 static int rand_initialize(void)
 {
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *entry;
+#endif	
 	init_std_data(&input_pool);
 	init_std_data(&blocking_pool);
 	init_std_data(&nonblocking_pool);
+#ifdef CONFIG_PROC_FS
+	entry = create_proc_entry("produce_ent", 0444, NULL);
+	if (entry)
+		entry->proc_fops = &prod_file_ops;
+	entry = create_proc_entry("consume_ent", 0444, NULL);
+	if (entry)
+		entry->proc_fops = &cons_file_ops;
+#endif
 	return 0;
 }
 module_init(rand_initialize);
@@ -918,8 +1092,20 @@ void rand_initialize_irq(int irq)
 	 * source.
 	 */
 	state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
-	if (state)
+	if (state) {
+		state->nbits = alloc_percpu(unsigned long);
 		irq_timer_state[irq] = state;
+	}
+}
+
+void rand_initialize_input()
+{
+	struct timer_rand_state *state = &input_timer_state;
+	state->nbits = alloc_percpu(unsigned long);
+	state->last_time = 0;
+	state->last_delta = 0;
+	state->last_delta2 = 0;
+	state->dont_count_entropy = 0;
 }
 
 #ifdef CONFIG_BLOCK
@@ -932,8 +1118,25 @@ void rand_initialize_disk(struct gendisk
 	 * source.
 	 */
 	state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
-	if (state)
+	if (state) {
+		state->nbits = alloc_percpu(unsigned long);
 		disk->random = state;
+		write_lock(&rand_disk_head.lock);	
+		list_add(&disk->random_list, &rand_disk_head.head_disk_list);
+		rand_disk_head.num_disk_list++;
+		write_unlock(&rand_disk_head.lock);
+	}
+}
+
+void rand_uninitialize_disk(struct gendisk *disk)
+{
+	struct timer_rand_state *state = disk->random;
+	write_lock(&rand_disk_head.lock);	
+	list_del(&disk->random_list);
+	rand_disk_head.num_disk_list--;
+	write_unlock(&rand_disk_head.lock);
+	free_percpu(state->nbits);
+	kfree(state);	
 }
 #endif
 
@@ -1077,6 +1280,9 @@ static long random_ioctl(struct file *f,
 			return -EPERM;
 		if (get_user(ent_count, p))
 			return -EFAULT;
+		spin_lock(&ent_ioctl_produce.lock);
+		ent_ioctl_produce.nbits += ent_count;
+		spin_unlock(&ent_ioctl_produce.lock);
 		credit_entropy_bits(&input_pool, ent_count);
 		return 0;
 	case RNDADDENTROPY:
@@ -1092,6 +1298,9 @@ static long random_ioctl(struct file *f,
 				    size);
 		if (retval < 0)
 			return retval;
+		spin_lock(&ent_ioctl_produce.lock);
+		ent_ioctl_produce.nbits += ent_count;
+		spin_unlock(&ent_ioctl_produce.lock);
 		credit_entropy_bits(&input_pool, ent_count);
 		return 0;
 	case RNDZAPENTCNT:
diff -upr -X linux-2.6.26.1.rand/Documentation/dontdiff linux-2.6.26.1/drivers/input/input.c linux-2.6.26.1.rand/drivers/input/input.c
--- linux-2.6.26.1/drivers/input/input.c	2008-08-02 02:58:24.000000000 +0400
+++ linux-2.6.26.1.rand/drivers/input/input.c	2008-08-23 16:35:10.000000000 +0400
@@ -914,6 +914,7 @@ static int __init input_proc_init(void)
 	if (!entry)
 		goto fail2;
 
+	rand_initialize_input();
 	return 0;
 
  fail2:	remove_proc_entry("devices", proc_bus_input_dir);
diff -upr -X linux-2.6.26.1.rand/Documentation/dontdiff linux-2.6.26.1/include/linux/genhd.h linux-2.6.26.1.rand/include/linux/genhd.h
--- linux-2.6.26.1/include/linux/genhd.h	2008-08-02 02:58:24.000000000 +0400
+++ linux-2.6.26.1.rand/include/linux/genhd.h	2008-08-23 15:00:21.000000000 +0400
@@ -61,6 +61,7 @@ enum {
 #include <linux/string.h>
 #include <linux/fs.h>
 #include <linux/workqueue.h>
+#include <linux/list.h>
 
 struct partition {
 	unsigned char boot_ind;		/* 0x80 - active */
@@ -130,6 +131,7 @@ struct gendisk {
 	struct kobject *slave_dir;
 
 	struct timer_rand_state *random;
+	struct list_head random_list;
 	int policy;
 
 	atomic_t sync_io;		/* RAID */
@@ -364,6 +366,7 @@ extern void set_disk_ro(struct gendisk *
 /* drivers/char/random.c */
 extern void add_disk_randomness(struct gendisk *disk);
 extern void rand_initialize_disk(struct gendisk *disk);
+extern void rand_uninitialize_disk(struct gendisk *disk);
 
 static inline sector_t get_start_sect(struct block_device *bdev)
 {
diff -upr -X linux-2.6.26.1.rand/Documentation/dontdiff linux-2.6.26.1/include/linux/random.h linux-2.6.26.1.rand/include/linux/random.h
--- linux-2.6.26.1/include/linux/random.h	2008-08-02 02:58:24.000000000 +0400
+++ linux-2.6.26.1.rand/include/linux/random.h	2008-08-23 17:32:50.000000000 +0400
@@ -43,6 +43,7 @@ struct rand_pool_info {
 #ifdef __KERNEL__
 
 extern void rand_initialize_irq(int irq);
+extern void rand_initialize_input(void);
 
 extern void add_input_randomness(unsigned int type, unsigned int code,
 				 unsigned int value);

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux