patch-2.3.44 linux/drivers/block/loop.c
Next file: linux/drivers/block/rd.c
Previous file: linux/arch/sparc64/kernel/irq.c
Back to the patch index
Back to the overall index
- Lines: 389
- Date:
Sat Feb 12 09:05:18 2000
- Orig file:
v2.3.43/linux/drivers/block/loop.c
- Orig date:
Thu Feb 10 17:11:07 2000
diff -u --recursive --new-file v2.3.43/linux/drivers/block/loop.c linux/drivers/block/loop.c
@@ -40,6 +40,10 @@
* it passes the underlying device's block number instead of the
* offset. This makes it change for a given block when the file is
* moved/restored/copied and also doesn't work over NFS.
+ * AV, Feb 11, 2000: for files we pass the page index now. It should fix the
+ * problem above. Since the granularity is PAGE_CACHE_SIZE now it seems to
+ * be correct way. OTOH, taking the thing from x86 to Alpha may become
+ * interesting, so we might want to rethink it.
*/
#include <linux/module.h>
@@ -77,10 +81,6 @@
#define FALSE 0
#define TRUE (!FALSE)
-/* Forward declaration of function to create missing blocks in the
- backing file (can happen if the backing file is sparse) */
-static int create_missing_block(struct loop_device *lo, int block, int blksize);
-
/*
* Transfer functions
*/
@@ -164,14 +164,109 @@
loop_sizes[lo->lo_number] = size;
}
+static int lo_send(struct loop_device *lo, char *data, int len, loff_t pos)
+{
+ struct file *file = lo->lo_backing_file; /* kudos to NFsckingS */
+ struct address_space *mapping = lo->lo_dentry->d_inode->i_mapping;
+ struct address_space_operations *aops = mapping->a_ops;
+ struct page *page;
+ char *kaddr;
+ unsigned long index;
+ unsigned size, offset;
+
+ index = pos >> PAGE_CACHE_SHIFT;
+ offset = pos & (PAGE_CACHE_SIZE - 1);
+ while (len > 0) {
+ size = PAGE_CACHE_SIZE - offset;
+ if (size > len)
+ size = len;
+
+ page = grab_cache_page(mapping, index);
+ if (!page)
+ goto fail;
+ if (aops->prepare_write(page, offset, offset+size))
+ goto unlock;
+ kaddr = (char*)page_address(page);
+ if ((lo->transfer)(lo, WRITE, kaddr+offset, data, size, index))
+ goto write_fail;
+ if (aops->commit_write(file, page, offset, offset+size))
+ goto unlock;
+ data += size;
+ len -= size;
+ offset = 0;
+ index++;
+ pos += size;
+ if (pos > lo->lo_dentry->d_inode->i_size)
+ lo->lo_dentry->d_inode->i_size = pos;
+ UnlockPage(page);
+ page_cache_release(page);
+ }
+ return 0;
+
+write_fail:
+ printk(KERN_ERR "loop: transfer error block %ld\n", index);
+ ClearPageUptodate(page);
+ kunmap(page);
+unlock:
+ UnlockPage(page);
+ page_cache_release(page);
+fail:
+ return -1;
+}
+
+struct lo_read_data {
+ struct loop_device *lo;
+ char *data;
+};
+
+static int lo_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
+{
+ char *kaddr;
+ unsigned long count = desc->count;
+ struct lo_read_data *p = (struct lo_read_data*)desc->buf;
+ struct loop_device *lo = p->lo;
+
+ if (size > count)
+ size = count;
+
+ kaddr = (char*)kmap(page);
+ if ((lo->transfer)(lo,READ,kaddr+offset,p->data,size,page->index)) {
+ size = 0;
+ printk(KERN_ERR "loop: transfer error block %ld\n",page->index);
+ desc->error = -EINVAL;
+ }
+ kunmap(page);
+
+ desc->count = count - size;
+ desc->written += size;
+ p->data += size;
+ return size;
+}
+
+static int lo_receive(struct loop_device *lo, char *data, int len, loff_t pos)
+{
+ struct file *file = lo->lo_backing_file;
+ struct lo_read_data cookie;
+ read_descriptor_t desc;
+
+ cookie.lo = lo;
+ cookie.data = data;
+ desc.written = 0;
+ desc.count = len;
+ desc.buf = (char*)&cookie;
+ desc.error = 0;
+ do_generic_file_read(file, &pos, &desc, lo_read_actor);
+ return desc.error;
+}
+
static void do_lo_request(request_queue_t * q)
{
- int real_block, block, offset, len, blksize, size;
+ int block, offset, len, blksize, size;
char *dest_addr;
struct loop_device *lo;
struct buffer_head *bh;
struct request *current_request;
- int block_present;
+ loff_t pos;
repeat:
INIT_REQUEST;
@@ -182,6 +277,18 @@
lo = &loop_dev[MINOR(current_request->rq_dev)];
if (!lo->lo_dentry || !lo->transfer)
goto error_out;
+ if (current_request->cmd == WRITE) {
+ if (lo->lo_flags & LO_FLAGS_READ_ONLY)
+ goto error_out;
+ } else if (current_request->cmd != READ) {
+ printk(KERN_ERR "unknown loop device command (%d)?!?", current_request->cmd);
+ goto error_out;
+ }
+
+ dest_addr = current_request->buffer;
+ len = current_request->current_nr_sectors << 9;
+ if (lo->lo_flags & LO_FLAGS_DO_BMAP)
+ goto file_backed;
blksize = BLOCK_SIZE;
if (blksize_size[MAJOR(lo->lo_device)]) {
@@ -189,9 +296,6 @@
if (!blksize)
blksize = BLOCK_SIZE;
}
-
- dest_addr = current_request->buffer;
-
if (blksize < 512) {
block = current_request->sector * (512/blksize);
offset = 0;
@@ -201,87 +305,68 @@
}
block += lo->lo_offset / blksize;
offset += lo->lo_offset % blksize;
- if (offset > blksize) {
+ if (offset >= blksize) {
block++;
offset -= blksize;
}
- len = current_request->current_nr_sectors << 9;
-
- if (current_request->cmd == WRITE) {
- if (lo->lo_flags & LO_FLAGS_READ_ONLY)
- goto error_out;
- } else if (current_request->cmd != READ) {
- printk(KERN_ERR "unknown loop device command (%d)?!?", current_request->cmd);
- goto error_out;
- }
spin_unlock_irq(&io_request_lock);
+
while (len > 0) {
size = blksize - offset;
if (size > len)
size = len;
- real_block = block;
- block_present = TRUE;
-
- if (lo->lo_flags & LO_FLAGS_DO_BMAP) {
- real_block = bmap(lo->lo_dentry->d_inode, block);
- if (!real_block) {
-
- /* The backing file is a sparse file and this block
- doesn't exist. If reading, return zeros. If
- writing, force the underlying FS to create
- the block */
- if (current_request->cmd == READ) {
- memset(dest_addr, 0, size);
- block_present = FALSE;
- } else {
- if (!create_missing_block(lo, block, blksize)) {
- goto error_out_lock;
- }
- real_block = bmap(lo->lo_dentry->d_inode, block);
- }
-
- }
+ bh = getblk(lo->lo_device, block, blksize);
+ if (!bh) {
+ printk(KERN_ERR "loop: device %s: getblk(-, %d, %d) returned NULL",
+ kdevname(lo->lo_device),
+ block, blksize);
+ goto error_out_lock;
}
-
- if (block_present) {
- bh = getblk(lo->lo_device, real_block, blksize);
- if (!bh) {
- printk(KERN_ERR "loop: device %s: getblk(-, %d, %d) returned NULL",
- kdevname(lo->lo_device),
- block, blksize);
- goto error_out_lock;
- }
- if (!buffer_uptodate(bh) && ((current_request->cmd == READ) ||
- (offset || (len < blksize)))) {
- ll_rw_block(READ, 1, &bh);
- wait_on_buffer(bh);
- if (!buffer_uptodate(bh)) {
- brelse(bh);
- goto error_out_lock;
- }
- }
-
- if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset,
- dest_addr, size, real_block)) {
- printk(KERN_ERR "loop: transfer error block %d\n", block);
+ if (!buffer_uptodate(bh) && ((current_request->cmd == READ) ||
+ (offset || (len < blksize)))) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
brelse(bh);
goto error_out_lock;
}
+ }
- if (current_request->cmd == WRITE) {
- mark_buffer_uptodate(bh, 1);
- mark_buffer_dirty(bh, 1);
- }
+ if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset,
+ dest_addr, size, block)) {
+ printk(KERN_ERR "loop: transfer error block %d\n", block);
brelse(bh);
+ goto error_out_lock;
}
+
+ if (current_request->cmd == WRITE) {
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 1);
+ }
+ brelse(bh);
dest_addr += size;
len -= size;
offset = 0;
block++;
}
+ goto done;
+
+file_backed:
+ pos = ((loff_t)current_request->sector << 9) + lo->lo_offset;
+ spin_unlock_irq(&io_request_lock);
+ if (current_request->cmd == WRITE) {
+ if (lo_send(lo, dest_addr, len, pos))
+ goto error_out_lock;
+ } else {
+ if (lo_receive(lo, dest_addr, len, pos))
+ goto error_out_lock;
+ }
+done:
spin_lock_irq(&io_request_lock);
+ current_request->sector += current_request->current_nr_sectors;
+ current_request->nr_sectors -= current_request->current_nr_sectors;
current_request->next=CURRENT;
CURRENT=current_request;
end_request(1);
@@ -295,61 +380,6 @@
goto repeat;
}
-static int create_missing_block(struct loop_device *lo, int block, int blksize)
-{
- struct file *file;
- loff_t new_offset;
- char zero_buf[1] = { 0 };
- ssize_t retval;
- mm_segment_t old_fs;
- struct inode *inode;
-
- file = lo->lo_backing_file;
- if (file == NULL) {
- printk(KERN_WARNING "loop: cannot create block - no backing file\n");
- return FALSE;
- }
-
- if (file->f_op == NULL) {
- printk(KERN_WARNING "loop: cannot create block - no file ops\n");
- return FALSE;
- }
-
- new_offset = block * blksize;
-
- if (file->f_op->llseek != NULL) {
- file->f_op->llseek(file, new_offset, 0);
- } else {
- /* Do what the default llseek() code would have done */
- file->f_pos = new_offset;
- file->f_reada = 0;
- file->f_version = ++event;
- }
-
- if (file->f_op->write == NULL) {
- printk(KERN_WARNING "loop: cannot create block - file not writeable\n");
- return FALSE;
- }
-
- old_fs = get_fs();
- set_fs(get_ds());
-
- inode = file->f_dentry->d_inode;
- down(&inode->i_sem);
- retval = file->f_op->write(file, zero_buf, 1, &file->f_pos);
- up(&inode->i_sem);
-
- set_fs(old_fs);
-
- if (retval < 0) {
- printk(KERN_WARNING "loop: cannot create block - FS write failed: code %Zi\n",
- retval);
- return FALSE;
- } else {
- return TRUE;
- }
-}
-
static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
{
struct file *file;
@@ -386,22 +416,12 @@
a file structure */
lo->lo_backing_file = NULL;
} else if (S_ISREG(inode->i_mode)) {
- /*
- * Total crap. We should just use pagecache instead of trying
- * to redirect on block level.
- */
- if (!inode->i_mapping->a_ops->bmap) {
- printk(KERN_ERR "loop: device has no block access/not implemented\n");
- goto out_putf;
- }
-
- /* Backed by a regular file - we need to hold onto
- a file structure for this file. We'll use it to
- write to blocks that are not already present in
- a sparse file. We create a new file structure
- based on the one passed to us via 'arg'. This is
- to avoid changing the file structure that the
- caller is using */
+ /* Backed by a regular file - we need to hold onto a file
+ structure for this file. Friggin' NFS can't live without
+ it on write and for reading we use do_generic_file_read(),
+ so... We create a new file structure based on the one
+ passed to us via 'arg'. This is to avoid changing the file
+ structure that the caller is using */
lo->lo_device = inode->i_dev;
lo->lo_flags = LO_FLAGS_DO_BMAP;
@@ -432,7 +452,6 @@
lo->lo_flags |= LO_FLAGS_READ_ONLY;
set_device_ro(dev, 1);
} else {
- vmtruncate (inode, 0);
set_device_ro(dev, 0);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)