patch-2.4.0-test10 linux/drivers/char/i810_rng.c

Next file: linux/drivers/char/ip2/i2ellis.c
Previous file: linux/drivers/char/generic_serial.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test9/linux/drivers/char/i810_rng.c linux/drivers/char/i810_rng.c
@@ -2,6 +2,7 @@
 
 	Hardware driver for Intel i810 Random Number Generator (RNG)
 	Copyright 2000 Jeff Garzik <jgarzik@mandrakesoft.com>
+	Copyright 2000 Philipp Rumpf <prumpf@tux.org>
 
 	Driver Web site:  http://gtf.org/garzik/drivers/i810_rng/
 
@@ -131,6 +132,9 @@
 	This will slow things down but guarantee that bad data is
 	never passed upstream.
 
+	* FIXME: module unload is racy.  To fix this, struct ctl_table
+	needs an owner member a la struct file_operations.
+
 	* Since the RNG is accessed from a timer as well as normal
 	kernel code, but not from interrupts, we use spin_lock_bh
 	in regular code, and spin_lock in the timer function, to
@@ -164,6 +168,17 @@
 	* Convert numeric globals to unsigned
 	* Module unload cleanup
 
+	Version 0.9.1:
+	* Support i815 chipsets too (Matt Sottek)
+	* Fix reference counting when statically compiled (prumpf)
+	* Rewrite rng_dev_read (prumpf)
+	* Make module races less likely (prumpf)
+	* Small miscellaneous bug fixes (prumpf)
+	* Use pci table for PCI id list
+
+	Version 0.9.2:
+	* Simplify open blocking logic
+
  */
 
 
@@ -187,7 +202,7 @@
 /*
  * core module and version information
  */
-#define RNG_VERSION "0.9.0"
+#define RNG_VERSION "0.9.2"
 #define RNG_MODULE_NAME "i810_rng"
 #define RNG_DRIVER_NAME   RNG_MODULE_NAME " hardware driver " RNG_VERSION
 #define PFX RNG_MODULE_NAME ": "
@@ -218,11 +233,6 @@
 
 
 /*
- * misc helper macros
- */
-#define arraysize(x)            (sizeof(x)/sizeof(*(x)))
-
-/*
  * prototypes
  */
 static void rng_fips_test_store (int rng_data);
@@ -281,8 +291,6 @@
 static struct timer_list rng_timer;	/* kernel timer for RNG hardware reads and tests */
 static struct pci_dev *rng_pdev;	/* Firmware Hub PCI device found during PCI probe */
 static struct semaphore rng_open_sem;	/* Semaphore for serializing rng_open/release */
-static wait_queue_head_t rng_open_wait;	/* Wait queue for serializing open/release */
-static int rng_open_mode;		/* Open mode (we only allow reads) */
 
 
 /*
@@ -363,7 +371,7 @@
 
 
 /*
- * rng_enable - enable or disable the RNG and internal timer
+ * rng_enable - enable or disable the RNG hardware
  */
 static int rng_enable (int enable)
 {
@@ -377,15 +385,13 @@
 	hw_status = rng_hwstatus ();
 
 	if (enable) {
-		rng_hw_enabled = 1;
+		rng_hw_enabled++;
 		MOD_INC_USE_COUNT;
 	} else {
-#ifndef __alpha__
-		if (GET_USE_COUNT (THIS_MODULE) > 0)
+		if (rng_hw_enabled) {
+			rng_hw_enabled--;
 			MOD_DEC_USE_COUNT;
-		if (GET_USE_COUNT (THIS_MODULE) == 0)
-			rng_hw_enabled = 0;
-#endif
+		}
 	}
 
 	if (rng_hw_enabled && ((hw_status & RNG_ENABLED) == 0)) {
@@ -407,7 +413,8 @@
 	else if (action == 2)
 		printk (KERN_INFO PFX "RNG h/w disabled\n");
 
-	if ((!!enable) != (!!(new_status & RNG_ENABLED))) {
+	/* too bad C doesn't have ^^ */
+	if ((!enable) != (!(new_status & RNG_ENABLED))) {
 		printk (KERN_ERR PFX "Unable to %sable the RNG\n",
 			enable ? "en" : "dis");
 		rc = -EIO;
@@ -429,6 +436,7 @@
 
 	DPRINTK ("ENTER\n");
 
+	MOD_INC_USE_COUNT;
 	spin_lock_bh (&rng_lock);
 	rng_enabled_sysctl = enabled_save = rng_timer_enabled;
 	spin_unlock_bh (&rng_lock);
@@ -456,6 +464,9 @@
 		spin_unlock_bh (&rng_lock);
 	}
 
+	/* This needs to be in a higher layer */
+	MOD_DEC_USE_COUNT;
+
 	DPRINTK ("EXIT, returning 0\n");
 	return 0;
 }
@@ -614,22 +625,17 @@
 	int rc = -EINVAL;
 
 	if ((filp->f_mode & FMODE_READ) == 0)
-		goto err_out_ret;
+		return rc;
 	if (filp->f_mode & FMODE_WRITE)
-		goto err_out_ret;
+		return rc;
 
 	/* wait for device to become free */
-	down (&rng_open_sem);
-	while (rng_open_mode & filp->f_mode) {
-		if (filp->f_flags & O_NONBLOCK) {
-			up (&rng_open_sem);
-			return -EWOULDBLOCK;
-		}
-		up (&rng_open_sem);
-		interruptible_sleep_on (&rng_open_wait);
-		if (signal_pending (current))
+	if (filp->f_flags & O_NONBLOCK) {
+		if (down_trylock (&rng_open_sem))
+			return -EAGAIN;
+	} else {
+		if (down_interruptible (&rng_open_sem))
 			return -ERESTARTSYS;
-		down (&rng_open_sem);
 	}
 
 	if (rng_enable (1)) {
@@ -637,87 +643,60 @@
 		goto err_out;
 	}
 
-	rng_open_mode |= filp->f_mode & (FMODE_READ | FMODE_WRITE);
-	up (&rng_open_sem);
 	return 0;
 
 err_out:
 	up (&rng_open_sem);
-err_out_ret:
 	return rc;
 }
 
 
 static int rng_dev_release (struct inode *inode, struct file *filp)
 {
-	down(&rng_open_sem);
-
 	rng_enable(0);
-	rng_open_mode &= (~filp->f_mode) & (FMODE_READ|FMODE_WRITE);
-
 	up (&rng_open_sem);
-	wake_up (&rng_open_wait);
 	return 0;
 }
 
 
-static ssize_t rng_dev_read (struct file *filp, char * buf, size_t size,
-			     loff_t *offp)
+static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size,
+			     loff_t * offp)
 {
-	int have_data, copied = 0;
-	u8 data=0;
-	u8 *page;
-
-	if (size < 1)
-		return 0;
-
-	page = (unsigned char *) get_free_page (GFP_KERNEL);
-	if (!page)
-		return -ENOMEM;
-
-read_loop:
-	/* using the fact that read() can return >0 but
-	 * less than the requested amount, we simply
-	 * read up to PAGE_SIZE or buffer size, whichever
-	 * is smaller, and return that data.
-	 */
-	if ((copied == size) || (copied == PAGE_SIZE)) {
-		size_t tmpsize = (copied == size) ? size : PAGE_SIZE;
-		int rc = copy_to_user (buf, page, tmpsize);
-		free_page ((long)page);
-		if (rc) return rc;
-		return tmpsize;
-	}
+	int have_data;
+	u8 data = 0;
+	ssize_t ret = 0;
 
-	spin_lock_bh (&rng_lock);
+	while (size) {
+		spin_lock_bh (&rng_lock);
 
-	have_data = 0;
-	if (rng_data_present ()) {
-		data = rng_data_read ();
-		have_data = 1;
-	}
+		have_data = 0;
+		if (rng_data_present ()) {
+			data = rng_data_read ();
+			have_data = 1;
+		}
 
-	spin_unlock_bh (&rng_lock);
+		spin_unlock_bh (&rng_lock);
 
-	if (have_data) {
-		page[copied] = data;
-		copied++;
-	} else {
-		if (filp->f_flags & O_NONBLOCK) {
-			free_page ((long)page);
-			return -EAGAIN;
+		if (have_data) {
+			if (put_user (data, buf++)) {
+				ret = ret ? : -EFAULT;
+				break;
+			}
+			size--;
+			ret++;
 		}
-	}
 
-	if (current->need_resched)
-		schedule ();
+		if (current->need_resched)
+			schedule ();
+
+		if (signal_pending (current))
+			return ret ? : -ERESTARTSYS;
 
-	if (signal_pending (current)) {
-		free_page ((long)page);
-		return -ERESTARTSYS;
+		if (filp->f_flags & O_NONBLOCK)
+			return ret ? : -EAGAIN;
 	}
 
-	goto read_loop;
+	return ret;
 }
 
 
@@ -793,8 +772,9 @@
  * want to register another driver on the same PCI id.
  */
 const static struct pci_device_id rng_pci_tbl[] __initdata = {
-        { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, },
-        { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x8086, 0x1130, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0, },
 };
 MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
@@ -832,14 +812,16 @@
 	DPRINTK ("ENTER\n");
 
 	init_MUTEX (&rng_open_sem);
-	init_waitqueue_head (&rng_open_wait);
 
-	pdev = pci_find_device (0x8086, 0x2418, NULL);
-	if (!pdev)
-		pdev = pci_find_device (0x8086, 0x2428, NULL);
-	if (!pdev)
-		return -ENODEV;
+	pci_for_each_dev(pdev) {
+		if (pci_match_device (rng_pci_tbl, pdev) != NULL)
+			goto match;
+	}
+
+	DPRINTK ("EXIT, returning -ENODEV\n");
+	return -ENODEV;
 
+match:
 	rc = rng_init_one (pdev);
 	if (rc)
 		return rc;
@@ -894,7 +876,7 @@
 *  4.11.1 (http://csrc.nist.gov/fips/fips1401.htm)
 *  The Monobit, Poker, Runs, and Long Runs tests are implemented below.
 *  This test is run at periodic intervals to verify
-*  data is sufficently random. If the tests are failed the RNG module
+*  data is sufficiently random. If the tests are failed the RNG module
 *  will no longer submit data to the entropy pool, but the tests will
 *  continue to run at the given interval. If at a later time the RNG
 *  passes all tests it will be re-enabled for the next period.
@@ -905,7 +887,7 @@
 *  disable the RNG, we will just leave it disabled for the period of
 *  time until the tests are rerun and passed.
 *
-*  For argument sake I tested /proc/urandom with these tests and it
+*  For argument sake I tested /dev/urandom with these tests and it
 *  took 142,095 tries before I got a failure, and urandom isn't as
 *  random as random :)
 */
@@ -923,7 +905,7 @@
 	int j;
 	static int last_bit = 0;
 
-	DPRINTK ("ENTER, rng_data = %d\n", rng_data & 0xFF);
+	DPRINTK ("ENTER, rng_data = %d\n", rng_data);
 
 	poker[rng_data >> 4]++;
 	poker[rng_data & 15]++;
@@ -1010,8 +992,8 @@
 	rng_trusted = rng_test;
 
 	/* finally, clear out FIPS variables for start of next run */
-	memset (&poker, 0, sizeof (poker));
-	memset (&runs, 0, sizeof (runs));
+	memset (poker, 0, sizeof (poker));
+	memset (runs, 0, sizeof (runs));
 	ones = 0;
 	rlength = -1;
 	current_bit = 0;
@@ -1019,4 +1001,3 @@
 
 	DPRINTK ("EXIT\n");
 }
-

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)