patch-2.4.10 linux/drivers/block/rd.c

Next file: linux/drivers/block/xd.c
Previous file: linux/drivers/block/ps2esdi.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.9/linux/drivers/block/rd.c linux/drivers/block/rd.c
@@ -100,7 +100,7 @@
 static int rd_blocksizes[NUM_RAMDISKS];		/* Size of 1024 byte blocks :)  */
 static int rd_kbsize[NUM_RAMDISKS];		/* Size in blocks of 1024 bytes */
 static devfs_handle_t devfs_handle;
-static struct inode *rd_inode[NUM_RAMDISKS];	/* Protected device inodes */
+static struct block_device *rd_bdev[NUM_RAMDISKS];/* Protected device data */
 
 /*
  * Parameters for the boot-loading of the RAM disk.  These are set by
@@ -187,6 +187,132 @@
 #endif
 
 /*
+ * Copyright (C) 2000 Linus Torvalds.
+ *               2000 Transmeta Corp.
+ * aops copied from ramfs.
+ */
+static int ramdisk_readpage(struct file *file, struct page * page)
+{
+	if (!Page_Uptodate(page)) {
+		memset(kmap(page), 0, PAGE_CACHE_SIZE);
+		kunmap(page);
+		flush_dcache_page(page);
+		SetPageUptodate(page);
+	}
+	UnlockPage(page);
+	return 0;
+}
+
+/*
+ * Writing: just make sure the page gets marked dirty, so that
+ * the page stealer won't grab it.
+ */
+static int ramdisk_writepage(struct page *page)
+{
+	SetPageDirty(page);
+	UnlockPage(page);
+	return 0;
+}
+
+static int ramdisk_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+	if (!Page_Uptodate(page)) {
+		void *addr = page_address(page);
+		memset(addr, 0, PAGE_CACHE_SIZE);
+		flush_dcache_page(page);
+		SetPageUptodate(page);
+	}
+	SetPageDirty(page);
+	return 0;
+}
+
+static int ramdisk_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+	return 0;
+}
+
+static struct address_space_operations ramdisk_aops = {
+	readpage: ramdisk_readpage,
+	writepage: ramdisk_writepage,
+	prepare_write: ramdisk_prepare_write,
+	commit_write: ramdisk_commit_write,
+};
+
+static int rd_blkdev_pagecache_IO(int rw, struct buffer_head * sbh, int minor)
+{
+	struct address_space * mapping;
+	unsigned long index;
+	int offset, size, err;
+
+	err = -EIO;
+	err = 0;
+	mapping = rd_bdev[minor]->bd_inode->i_mapping;
+
+	index = sbh->b_rsector >> (PAGE_CACHE_SHIFT - 9);
+	offset = (sbh->b_rsector << 9) & ~PAGE_CACHE_MASK;
+	size = sbh->b_size;
+
+	do {
+		int count;
+		struct page ** hash;
+		struct page * page;
+		char * src, * dst;
+		int unlock = 0;
+
+		count = PAGE_CACHE_SIZE - offset;
+		if (count > size)
+			count = size;
+		size -= count;
+
+		hash = page_hash(mapping, index);
+		page = __find_get_page(mapping, index, hash);
+		if (!page) {
+			page = grab_cache_page(mapping, index);
+			err = -ENOMEM;
+			if (!page)
+				goto out;
+			err = 0;
+
+			if (!Page_Uptodate(page)) {
+				memset(kmap(page), 0, PAGE_CACHE_SIZE);
+				kunmap(page);
+				flush_dcache_page(page);
+				SetPageUptodate(page);
+			}
+
+			unlock = 1;
+		}
+
+		index++;
+
+		if (rw == READ) {
+			src = kmap(page);
+			src += offset;
+			dst = bh_kmap(sbh);
+		} else {
+			dst = kmap(page);
+			dst += offset;
+			src = bh_kmap(sbh);
+		}
+		offset = 0;
+
+		memcpy(dst, src, count);
+
+		kunmap(page);
+		bh_kunmap(sbh);
+
+		if (rw != READ)
+			SetPageDirty(page);
+		if (unlock)
+			UnlockPage(page);
+		__free_page(page);
+	} while (size);
+
+ out:
+	return err;
+}
+
+/*
  *  Basically, my strategy here is to set up a buffer-head which can't be
  *  deleted, and make that my Ramdisk.  If the request is outside of the
  *  allocated size, we must get rid of it...
@@ -198,10 +324,7 @@
 {
 	unsigned int minor;
 	unsigned long offset, len;
-	struct buffer_head *rbh;
-	char *bdata;
 
-	
 	minor = MINOR(sbh->b_rdev);
 
 	if (minor >= NUM_RAMDISKS)
@@ -221,20 +344,8 @@
 		goto fail;
 	}
 
-	rbh = getblk(sbh->b_rdev, sbh->b_rsector/(sbh->b_size>>9), sbh->b_size);
-	/* I think that it is safe to assume that rbh is not in HighMem, though
-	 * sbh might be - NeilBrown
-	 */
-	bdata = bh_kmap(sbh);
-	if (rw == READ) {
-		if (sbh != rbh)
-			memcpy(bdata, rbh->b_data, rbh->b_size);
-	} else
-		if (sbh != rbh)
-			memcpy(rbh->b_data, bdata, rbh->b_size);
-	bh_kunmap(sbh);
-	mark_buffer_protected(rbh);
-	brelse(rbh);
+	if (rd_blkdev_pagecache_IO(rw, sbh, minor))
+		goto fail;
 
 	sbh->b_end_io(sbh,1);
 	return 0;
@@ -245,10 +356,11 @@
 
 static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
+	int error = -EINVAL;
 	unsigned int minor;
 
 	if (!inode || !inode->i_rdev) 	
-		return -EINVAL;
+		goto out;
 
 	minor = MINOR(inode->i_rdev);
 
@@ -259,26 +371,29 @@
 			/* special: we want to release the ramdisk memory,
 			   it's not like with the other blockdevices where
 			   this ioctl only flushes away the buffer cache. */
-			if ((atomic_read(&inode->i_bdev->bd_openers) > 2))
-				return -EBUSY;
-			destroy_buffers(inode->i_rdev);
-			rd_blocksizes[minor] = 0;
+			error = -EBUSY;
+			down(&inode->i_bdev->bd_sem);
+			if (inode->i_bdev->bd_openers <= 2) {
+				truncate_inode_pages(inode->i_mapping, 0);
+				error = 0;
+			}
+			up(&inode->i_bdev->bd_sem);
 			break;
-
          	case BLKGETSIZE:   /* Return device size */
-			if (!arg)  return -EINVAL;
-			return put_user(rd_kbsize[minor] << 1, (long *) arg);
-
+			if (!arg)
+				break;
+			error = put_user(rd_kbsize[minor] << 1, (long *) arg);
+			break;
+         	case BLKGETSIZE64:
+			error = put_user((u64)rd_kbsize[minor]<<10, (u64*)arg);
+			break;
 		case BLKROSET:
 		case BLKROGET:
 		case BLKSSZGET:
-			return blk_ioctl(inode->i_rdev, cmd, arg);
-
-		default:
-			return -EINVAL;
+			error = blk_ioctl(inode->i_rdev, cmd, arg);
 	};
-
-	return 0;
+out:
+	return error;
 }
 
 
@@ -304,12 +419,11 @@
 
 	lock_kernel();
 	if (!--initrd_users) {
-		blkdev_put(inode->i_bdev, BDEV_FILE);
-		iput(inode);
 		free_initrd_mem(initrd_start, initrd_end);
 		initrd_start = 0;
 	}
 	unlock_kernel();
+	blkdev_put(inode->i_bdev, BDEV_FILE);
 	return 0;
 }
 
@@ -324,8 +438,10 @@
 
 static int rd_open(struct inode * inode, struct file * filp)
 {
+	int unit = DEVICE_NR(inode->i_rdev);
+
 #ifdef CONFIG_BLK_DEV_INITRD
-	if (DEVICE_NR(inode->i_rdev) == INITRD_MINOR) {
+	if (unit == INITRD_MINOR) {
 		if (!initrd_start) return -ENODEV;
 		initrd_users++;
 		filp->f_op = &initrd_fops;
@@ -333,16 +449,16 @@
 	}
 #endif
 
-	if (DEVICE_NR(inode->i_rdev) >= NUM_RAMDISKS)
+	if (unit >= NUM_RAMDISKS)
 		return -ENXIO;
 
 	/*
 	 * Immunize device against invalidate_buffers() and prune_icache().
 	 */
-	if (rd_inode[DEVICE_NR(inode->i_rdev)] == NULL) {
-		if (!inode->i_bdev) return -ENXIO;
-		if ((rd_inode[DEVICE_NR(inode->i_rdev)] = igrab(inode)) != NULL)
-			atomic_inc(&rd_inode[DEVICE_NR(inode->i_rdev)]->i_bdev->bd_openers);
+	if (rd_bdev[unit] == NULL) {
+		rd_bdev[unit] = bdget(kdev_t_to_nr(inode->i_rdev));
+		rd_bdev[unit]->bd_openers++;
+		rd_bdev[unit]->bd_inode->i_mapping->a_ops = &ramdisk_aops;
 	}
 
 	MOD_INC_USE_COUNT;
@@ -356,7 +472,7 @@
 	return 0;
 }
 
-static struct block_device_operations fd_fops = {
+static struct block_device_operations rd_bd_op = {
 	open:		rd_open,
 	release:	rd_release,
 	ioctl:		rd_ioctl,
@@ -369,13 +485,10 @@
 	int i;
 
 	for (i = 0 ; i < NUM_RAMDISKS; i++) {
-		if (rd_inode[i]) {
-			/* withdraw invalidate_buffers() and prune_icache() immunity */
-			atomic_dec(&rd_inode[i]->i_bdev->bd_openers);
-			/* remove stale pointer to module address space */
-			rd_inode[i]->i_bdev->bd_op = NULL;
-			iput(rd_inode[i]);
-		}
+		struct block_device *bdev = rd_bdev[i];
+		rd_bdev[i] = NULL;
+		if (bdev)
+			blkdev_put(bdev, BDEV_FILE);
 		destroy_buffers(MKDEV(MAJOR_NR, i));
 	}
 
@@ -400,7 +513,7 @@
 		rd_blocksize = BLOCK_SIZE;
 	}
 
-	if (register_blkdev(MAJOR_NR, "ramdisk", &fd_fops)) {
+	if (register_blkdev(MAJOR_NR, "ramdisk", &rd_bd_op)) {
 		printk("RAMDISK: Could not get major %d", MAJOR_NR);
 		return -EIO;
 	}
@@ -418,14 +531,14 @@
 	devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS,
 			       DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 			       S_IFBLK | S_IRUSR | S_IWUSR,
-			       &fd_fops, NULL);
+			       &rd_bd_op, NULL);
 
 	for (i = 0; i < NUM_RAMDISKS; i++)
-		register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &fd_fops, rd_size<<1);
+		register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &rd_bd_op, rd_size<<1);
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	/* We ought to separate initrd operations here */
-	register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &fd_fops, rd_size<<1);
+	register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &rd_bd_op, rd_size<<1);
 #endif
 
 	hardsect_size[MAJOR_NR] = rd_hardsec;		/* Size of the RAM disk blocks */
@@ -451,6 +564,8 @@
 MODULE_PARM     (rd_blocksize, "i");
 MODULE_PARM_DESC(rd_blocksize, "Blocksize of each RAM disk in bytes.");
 
+MODULE_LICENSE("GPL");
+
 /* End of non-loading portions of the RAM disk driver */
 
 #ifdef RD_LOADER 
@@ -595,8 +710,10 @@
 	outfile.f_op = &def_blk_fops;
 	init_special_inode(out_inode, S_IFBLK | S_IRUSR | S_IWUSR, kdev_t_to_nr(ram_device));
 
-	if (blkdev_open(inode, &infile) != 0)
+	if (blkdev_open(inode, &infile) != 0) {
+		iput(out_inode);
 		goto free_inode;
+	}
 	if (blkdev_open(out_inode, &outfile) != 0)
 		goto free_inodes;
 
@@ -659,14 +776,15 @@
 		if (i && (i % devblocks == 0)) {
 			printk("done disk #%d.\n", i/devblocks);
 			rotate = 0;
-			invalidate_buffers(device);
-			if (infile.f_op->release)
-				infile.f_op->release(inode, &infile);
+			if (infile.f_op->release(inode, &infile) != 0) {
+				printk("Error closing the disk.\n");
+				goto noclose_input;
+			}
 			printk("Please insert disk #%d and press ENTER\n", i/devblocks+1);
 			wait_for_keypress();
 			if (blkdev_open(inode, &infile) != 0)  {
 				printk("Error opening disk.\n");
-				goto done;
+				goto noclose_input;
 			}
 			infile.f_pos = 0;
 			printk("Loading disk #%d... ", i/devblocks+1);
@@ -684,18 +802,20 @@
 	kfree(buf);
 
 successful_load:
-	invalidate_buffers(device);
 	ROOT_DEV = MKDEV(MAJOR_NR, unit);
 	if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, "rd/0");
 
 done:
-	if (infile.f_op->release)
-		infile.f_op->release(inode, &infile);
+	infile.f_op->release(inode, &infile);
+noclose_input:
+	blkdev_close(out_inode, &outfile);
+	iput(inode);
+	iput(out_inode);
 	set_fs(fs);
 	return;
 free_inodes: /* free inodes on error */ 
 	iput(out_inode);
-	blkdev_put(inode->i_bdev, BDEV_FILE);
+	infile.f_op->release(inode, &infile);
 free_inode:
 	iput(inode);
 }

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