diff -Naur -X exclude-files ac_cur/arch/um/drivers/ubd.c ac/arch/um/drivers/ubd.c --- ac_cur/arch/um/drivers/ubd.c Mon Jul 16 11:05:05 2001 +++ ac/arch/um/drivers/ubd.c Mon Jul 16 11:17:57 2001 @@ -15,6 +15,7 @@ #include "linux/proc_fs.h" #include "linux/ctype.h" #include "linux/capability.h" +#include "linux/mm.h" #include "asm/segment.h" #include "asm/uaccess.h" #include "asm/irq.h" @@ -79,17 +80,13 @@ #define OPEN_FLAGS O_RDWR #endif -#define DEFAULT_UBD { \ - file: NULL, \ - is_dir: 0, \ - count: 0, \ - fd: -1, \ - size: -1, \ - boot_openflags: OPEN_FLAGS, \ - openflags: OPEN_FLAGS, \ - real: NULL, \ - fake: NULL, \ -} +struct cow { + char *file; + int fd; + unsigned long *bitmap; + int bitmap_len; + int data_offset; +}; struct ubd { char *file; @@ -101,17 +98,42 @@ int openflags; devfs_handle_t real; devfs_handle_t fake; -} ubd_dev[MAX_DEV] = { + struct cow cow; +}; + +#define DEFAULT_COW { \ + file: NULL, \ + fd: -1, \ + bitmap: NULL, \ + bitmap_len: 0, \ + data_offset: 0, \ +} + +#define DEFAULT_UBD { \ + file: NULL, \ + is_dir: 0, \ + count: 0, \ + fd: -1, \ + size: -1, \ + boot_openflags: OPEN_FLAGS, \ + openflags: OPEN_FLAGS, \ + real: NULL, \ + fake: NULL, \ + cow: DEFAULT_COW, \ +} + +struct ubd ubd_dev[MAX_DEV] = { { - file: "root_fs", - is_dir: 0, - count: 0, - fd: -1, - size: 0, - boot_openflags: OPEN_FLAGS, - openflags: OPEN_FLAGS, - real: NULL, - fake: NULL, + file: "root_fs", + is_dir: 0, + count: 0, + fd: -1, + size: 0, + boot_openflags: OPEN_FLAGS, + openflags: OPEN_FLAGS, + real: NULL, + fake: NULL, + cow: DEFAULT_COW, }, [ 1 ... MAX_DEV - 1 ] = DEFAULT_UBD }; @@ -176,10 +198,20 @@ __setup("fake_ide", fake_ide_setup); +static char *ubd_strdup(char *string) +{ + char *new; + + new = kmalloc(strlen(string) + 1, GFP_KERNEL); + if(new == NULL) return(NULL); + strcpy(new, string); + return(new); +} + static int ubd_setup_common(char *str, int *index_out, int copy_name) { - int n; - int sync, perm = O_RDWR; + char *backing_file; + int n, sync, perm = O_RDWR; if(index_out) *index_out = -1; n = *str++; @@ -224,12 +256,23 @@ printk("ubd_setup : Expected '='\n"); return(1); } + backing_file = strchr(str, ','); + if(backing_file){ + *backing_file = '\0'; + backing_file++; + } if(copy_name){ - ubd_dev[n].file = kmalloc(strlen(str) + 1, GFP_ATOMIC); + ubd_dev[n].file = ubd_strdup(str); if(ubd_dev[n].file == NULL) return(0); - strcpy(ubd_dev[n].file, str); + if(backing_file != NULL){ + ubd_dev[n].cow.file = ubd_strdup(backing_file); + if(ubd_dev[n].cow.file == NULL) return(0); + } + } + else { + ubd_dev[n].file = str; + ubd_dev[n].cow.file = backing_file; } - else ubd_dev[n].file = str; ubd_dev[n].boot_openflags = perm | sync; return(1); } @@ -286,9 +329,11 @@ "errno = %d\n", getpid(), errno); return; } - if((req.offset != ((__u64) (CURRENT->sector)) << 9) || + + if((req.offset != ((__u64) (CURRENT->sector)) << 9) || (req.length != (CURRENT->current_nr_sectors) << 9)) panic("I/O op mismatch"); + ubd_finish(); reactivate_fd(thread_fds[0]); if (!QUEUE_EMPTY) do_ubd_request(NULL); @@ -430,8 +475,73 @@ __initcall(ubd_init); +static void ubd_close(struct ubd *dev) +{ + close_fd(dev->fd); + if(dev->cow.file != NULL) close_fd(dev->cow.fd); +} + +static int ubd_open_dev(struct ubd *dev) +{ + char *backing_file; + int err, len, order, flags, n; + + dev->fd = open_ubd_file(dev->file, &dev->openflags, &backing_file, + &dev->cow.bitmap_len, &dev->cow.data_offset); + + if((dev->fd == -ENOENT) && (dev->cow.file != NULL)){ + printk(KERN_INFO "Creating \"%s\" as COW file for \"%s\"\n", + dev->file, dev->cow.file); + n = dev - ubd_dev; + dev->fd = create_cow_file(dev->file, dev->cow.file, 1 << 9, + &dev->cow.bitmap_len, + &dev->cow.data_offset); + if(dev->fd < 0){ + printk(KERN_ERR "Creation of COW file \"%s\" failed, " + "errno = %d\n", dev->file, -dev->fd); + } + } + + if(dev->fd < 0) return(dev->fd); + + if(dev->cow.file == NULL) dev->cow.file = backing_file; + if((backing_file != NULL) && strcmp(dev->cow.file, backing_file)){ + printk(KERN_WARNING "Backing file mismatch - \"%s\" " + "requested, \"%s\" specified in COW header of \"%s\"\n", + dev->cow.file, backing_file, dev->file); + printk(KERN_WARNING "Using \"%s\"\n", backing_file); + dev->cow.file = backing_file; + } + + if(dev->cow.file != NULL){ + len = dev->cow.bitmap_len / PAGE_SIZE; + order = 0; + while(len){ + order++; + len >>= 1; + } + err = -ENOMEM; + dev->cow.bitmap = (void *) __get_free_pages(GFP_KERNEL, order); + if(dev->cow.bitmap == NULL) goto error; + + err = read_cow_bitmap(dev->fd, dev->cow.bitmap, + dev->cow.bitmap_len); + if(err) goto error; + + flags = O_RDONLY; + err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL); + if(err < 0) goto error; + dev->cow.fd = err; + } + return(0); + error: + close_fd(dev->fd); + return(err); +} + static int ubd_open(struct inode * inode, struct file * filp) { + char *file; int n; n = DEVICE_NR(inode->i_rdev); @@ -441,25 +551,23 @@ ubd_dev[n].is_dir = 1; return(0); } - ubd_dev[n].size = file_size(ubd_dev[n].file); - if(ubd_dev[n].size < 0) return(ubd_dev[n].size); - ubd_part[n].start_sect = 0; - ubd_part[n].nr_sects = ubd_dev[n].size / blk_sizes[n]; - sizes[n] = ubd_dev[n].size / BLOCK_SIZE; ubd_dev[n].openflags = ubd_dev[n].boot_openflags; - if(ubd_dev[n].count == 0){ - if((ubd_dev[n].fd = open_ubd_fs(ubd_dev[n].file, - &ubd_dev[n].openflags)) < 0){ - printk("ubd%d: Can't open \"%s\": errno = %d\n", n, - ubd_dev[n].file, -ubd_dev[n].fd); - } + if((ubd_dev[n].count == 0) && (ubd_open_dev(&ubd_dev[n]) < 0)){ + printk(KERN_ERR "ubd%d: Can't open \"%s\": errno = %d\n", n, + ubd_dev[n].file, -ubd_dev[n].fd); } if(ubd_dev[n].fd < 0) return -ENODEV; + file = ubd_dev[n].cow.file ? ubd_dev[n].cow.file : ubd_dev[n].file; + ubd_dev[n].size = file_size(file); + if(ubd_dev[n].size < 0) return(ubd_dev[n].size); + ubd_part[n].start_sect = 0; + ubd_part[n].nr_sects = ubd_dev[n].size / blk_sizes[n]; + sizes[n] = ubd_dev[n].size / BLOCK_SIZE; ubd_dev[n].count++; if ((filp->f_mode & FMODE_WRITE) && ((ubd_dev[n].openflags & ~O_SYNC) == O_RDONLY)){ - if(--ubd_dev[n].count == 0) close_fd(ubd_dev[n].fd); + if(--ubd_dev[n].count == 0) ubd_close(&ubd_dev[n]); return -EROFS; } return(0); @@ -472,14 +580,48 @@ n = DEVICE_NR(inode->i_rdev); if(n > MAX_DEV) return -ENODEV; - if(--ubd_dev[n].count == 0) close_fd(ubd_dev[n].fd); + if(--ubd_dev[n].count == 0) ubd_close(&ubd_dev[n]); return(0); } +int cow_read = 0; +int cow_write = 0; + +void cowify_req(struct io_thread_req *req, struct ubd *dev) +{ + int i, update_bitmap, sector = req->offset >> 9; + + if(req->length > (sizeof(req->sector_mask) * 8) << 9) + panic("Operation too long"); + if(req->read) { + for(i = 0; i < req->length >> 9; i++){ + if(ubd_test_bit(sector + i, dev->cow.bitmap)){ + ubd_set_bit(i, &req->sector_mask); + cow_read++; + } + } + } + else { + update_bitmap = 0; + for(i = 0; i < req->length >> 9; i++){ + cow_write++; + ubd_set_bit(i, &req->sector_mask); + if(!ubd_test_bit(sector + i, dev->cow.bitmap)) + update_bitmap = 1; + ubd_set_bit(sector + i, dev->cow.bitmap); + } + if(update_bitmap){ + req->bitmap_start = sector / (sizeof(long) * 8); + req->bitmap_end = (sector + i) / (sizeof(long) * 8); + req->bitmap_end++; + } + } +} + static void do_one_request(void) { struct io_thread_req req; - int block, nsect, dev; + int block, nsect, dev, n; if(CURRENT->rq_status == RQ_INACTIVE) return; if (DEVICE_INTR) return; @@ -493,32 +635,43 @@ end_request(1); return; } + req.read = (CURRENT->cmd == READ); - req.fd = ubd_dev[dev].fd; + req.fds[0] = (ubd_dev[dev].cow.file != NULL) ? + ubd_dev[dev].cow.fd : ubd_dev[dev].fd; + req.fds[1] = ubd_dev[dev].fd; + req.offsets[0] = 0; + req.offsets[1] = ubd_dev[dev].cow.data_offset; req.offset = ((__u64) block) << 9; req.length = nsect << 9; req.buffer = CURRENT->buffer; - req.req = CURRENT; + req.sectorsize = 1 << 9; + req.sector_mask = 0; + req.bitmap = ubd_dev[dev].cow.bitmap; + req.bitmap_start = -1; + req.bitmap_end = -1; + + if(ubd_dev[dev].cow.file != NULL) cowify_req(&req, &ubd_dev[dev]); + if(thread_fds[0] == -1){ do_io(&req); ubd_finish(); } else { SET_INTR(ubd_handler); - write_ubd_fs(thread_fds[1], (char *) &req, sizeof(req)); + n = write_ubd_fs(thread_fds[1], (char *) &req, sizeof(req)); + if(n != sizeof(req)) + printk("write to io thread failed - returned %d, " + "errno %d\n", n, errno); } } static void do_ubd_request(request_queue_t * q) { if(thread_fds[0] == -1){ - while(!QUEUE_EMPTY){ - do_one_request(); - } - } - else { - do_one_request(); + while(!QUEUE_EMPTY) do_one_request(); } + else do_one_request(); } static int ubd_ioctl(struct inode * inode, struct file * file, diff -Naur -X exclude-files ac_cur/arch/um/drivers/ubd_user.c ac/arch/um/drivers/ubd_user.c --- ac_cur/arch/um/drivers/ubd_user.c Mon Jul 16 11:04:43 2001 +++ ac/arch/um/drivers/ubd_user.c Mon Jul 16 11:17:57 2001 @@ -1,5 +1,6 @@ /* * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com) * Licensed under the GPL */ @@ -7,26 +8,158 @@ #include #include #include +#include #include #include #include +#include +#include #include "user_util.h" #include "user.h" #include "ubd_user.h" extern void panic(char *fmt, ...); -int open_ubd_fs(char *file, int *openflags) +struct cow_header { + int magic; + int version; + char backing_file[256]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +#define COW_MAGIC 0x4f4f4f4d /* mooo */ +#define COW_VERSION 1 + +static void sizes(__u64 size, int sectorsize, int *bitmap_len_out, + int *data_offset_out) { - int fd; + *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); + + *data_offset_out = sizeof(struct cow_header) + *bitmap_len_out; + *data_offset_out = (*data_offset_out + sectorsize - 1) / sectorsize; + *data_offset_out *= sectorsize; +} + +int open_ubd_file(char *file, int *openflags, char **backing_file_out, + int *bitmap_len_out, int *data_offset_out) +{ + struct cow_header header; + struct stat64 buf; + int fd, err, mode = 0644; + + if(backing_file_out != NULL) *backing_file_out = NULL; + if((fd = open64(file, *openflags, mode)) < 0){ + if(((*openflags & O_ACCMODE) != O_RDWR) || + ((errno != EROFS) && (errno != EACCES))) return(-errno); + *openflags &= ~O_ACCMODE; + *openflags |= O_RDONLY; + if((fd = open64(file, *openflags, mode)) < 0) return(-errno); + } + if(backing_file_out == NULL) return(fd); + err = -EINVAL; + if(read(fd, &header, sizeof(header)) != sizeof(header)){ + printk("Failed to check COW header in \"%s\", errno = %d\n", + file, errno); + goto error; + } + if(header.magic != COW_MAGIC){ + *backing_file_out = NULL; + return(fd); + } + + err = stat64(header.backing_file, &buf); + if(err) goto error; + + err = -EINVAL; + if(buf.st_size != header.size){ + printk("Size mismatch (%ld vs %ld) of COW header vs backing " + "file\n", buf.st_size, header.size); + goto error; + } + if(buf.st_mtime != header.mtime){ + printk("mtime mismatch (%ld vs %ld) of COW header vs backing " + "file\n", buf.st_mtime, header.mtime); + goto error; + } - if((fd = open64(file, *openflags, 0)) < 0){ - if((*openflags != O_RDWR) || - ((errno != EROFS) && (errno != EACCES))) return(-errno); - *openflags = O_RDONLY; - if((fd = open64(file, *openflags, 0)) < 0) return(-errno); + *backing_file_out = um_kmalloc(strlen(header.backing_file) + 1); + if(*backing_file_out == NULL){ + close(fd); + return(-ENOMEM); } + strcpy(*backing_file_out, header.backing_file); + + sizes(header.size, header.sectorsize, bitmap_len_out, data_offset_out); + + return(fd); + error: + close(fd); + return(err); +} + +int read_cow_bitmap(int fd, void *buf, int len) +{ + int err; + + err = lseek64(fd, sizeof(struct cow_header), SEEK_SET); + if(err != sizeof(struct cow_header)) return(-errno); + err = read(fd, buf, len); + if(err < 0) return(-errno); + return(0); +} + +int create_cow_file(char *cow_file, char *backing_file, int sectorsize, + int *bitmap_len_out, int *data_offset_out) +{ + struct cow_header header; + struct stat64 buf; + __u64 blocks; + long zero; + int err, fd, i, flags; + + flags = O_RDWR | O_CREAT; + fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL); + if(fd < 0) return(fd); + + header.magic = COW_MAGIC; + header.version = COW_VERSION; + + err = -EINVAL; + if(strlen(backing_file) > sizeof(header.backing_file) - 1){ + printk("Backing file name \"%s\" is too long - names are " + "limited to %d characters\n", backing_file, + sizeof(header.backing_file) - 1); + goto error; + } + strcpy(header.backing_file, backing_file); + + err = stat64(header.backing_file, &buf); + if(err < 0) goto error; + + header.mtime = buf.st_mtime; + header.size = buf.st_size; + header.sectorsize = sectorsize; + + err = write(fd, &header, sizeof(header)); + if(err != sizeof(header)) goto error; + + blocks = (header.size + sectorsize - 1) / sectorsize; + blocks = (blocks + sizeof(long) * 8 - 1) / (sizeof(long) * 8); + zero = 0; + for(i = 0; i < blocks; i++){ + err = write(fd, &zero, sizeof(zero)); + if(err != sizeof(zero)) goto error; + } + + sizes(header.size, header.sectorsize, bitmap_len_out, data_offset_out); + return(fd); + + error: + close(fd); + return(err); } int read_ubd_fs(int fd, void *buffer, int len) @@ -47,30 +180,72 @@ return(S_ISDIR(buf.st_mode)); } +unsigned long last_offset = -1; + int do_io(struct io_thread_req *req) { - int n; + char *buf; + unsigned long len; + int n, off, nsectors, start, end, bit; + + nsectors = req->length / req->sectorsize; + start = 0; + do { + bit = ubd_test_bit(start, &req->sector_mask); + end = start; + while((end < nsectors) && + (ubd_test_bit(end, &req->sector_mask) == bit)) + end++; + + if(end != nsectors) + printk("end != nsectors\n"); + off = req->offset + req->offsets[bit]; + len = (end - start) * req->sectorsize; + buf = &req->buffer[start * req->sectorsize]; + last_offset = off; + + if(lseek64(req->fds[bit], off, SEEK_SET) != off){ + printk("do_io - lseek failed : errno = %d\n", + errno); + return(-1); + } + if(req->read){ + n = read(req->fds[bit], buf, len); + if(n <= 0){ + printk("do_io - read returned %d : " + "errno = %d fd = %d\n", n, + errno, req->fds[bit]); + return(-1); + } + else if(n != len) memset(&buf[n], 0, len - n); + } + else { + n = write(req->fds[bit], buf, len); + if(n != len){ + printk("do_io - write returned %d : " + "errno = %d fd = %d\n", n, + errno, req->fds[bit]); + return(-1); + } + } - if(lseek64(req->fd, req->offset, SEEK_SET) < 0){ - printk("do_io - lseek failed : errno = %d\n", - errno); - return(-1); - } - if(req->read){ - n = read(req->fd, req->buffer, req->length); - if(n != req->length) - memset(&req->buffer[n], 0, req->length - n); - else if(n <= 0){ - printk("do_io - read returned %d : " - "errno = %d\n", n, errno); + start = end; + } while(start < nsectors); + + if(req->bitmap_start != -1){ + off = req->bitmap_start * sizeof(req->bitmap[0]); + off += sizeof(struct cow_header); + if(lseek64(req->fds[1], off, SEEK_SET) != off){ + printk("do_io - bitmap lseek failed : errno = %d\n", + errno); return(-1); } - } - else { - n = write(req->fd, req->buffer, req->length); - if(n != req->length){ - printk("do_io - write returned %d : " - "errno = %d\n", n, errno); + len = (req->bitmap_end - req->bitmap_start) * + sizeof(req->bitmap[0]); + n = write(req->fds[1], &req->bitmap[req->bitmap_start], len); + if(n != len){ + printk("do_io - bitmap update returned %d : " + "errno = %d fd = %d\n", n, errno, req->fds[1]); return(-1); } } diff -Naur -X exclude-files ac_cur/arch/um/include/ubd_user.h ac/arch/um/include/ubd_user.h --- ac_cur/arch/um/include/ubd_user.h Mon Jul 16 11:03:59 2001 +++ ac/arch/um/include/ubd_user.h Mon Jul 16 11:17:57 2001 @@ -1,28 +1,58 @@ /* * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 RidgeRun, Inc (glonnon@ridgerun.com) * Licensed under the GPL */ #ifndef __UM_UBD_USER_H #define __UM_UBD_USER_H -#include "asm/types.h" struct io_thread_req { int read; - int fd; - __u64 offset; + int fds[2]; + unsigned long offsets[2]; + unsigned long long offset; unsigned long length; char *buffer; - void *req; + int sectorsize; + unsigned long sector_mask; + unsigned long *bitmap; + unsigned long bitmap_start; + unsigned long bitmap_end; }; -extern int open_ubd_fs(char *file, int *openflags); +extern int open_ubd_file(char *file, int *openflags, char **backing_file_out, + int *bitmap_len_out, int *data_offset_out); +extern int create_cow_file(char *cow_file, char *backing_file, int sectorsize, + int *bitmap_len_out, int *data_offset_out); +extern int read_cow_bitmap(int fd, void *buf, int len); extern int read_ubd_fs(int fd, void *buffer, int len); extern int write_ubd_fs(int fd, char *buffer, int len); extern int start_io_thread(unsigned long sp, int *fds_out); extern int do_io(struct io_thread_req *req); extern int ubd_is_dir(char *file); + +static inline int ubd_test_bit(int bit, unsigned long *data) +{ + int bits, n, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + return((data[n] & (1 << off)) != 0); +} + +static inline void ubd_set_bit(int bit, unsigned long *data) +{ + int bits, n, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + data[n] |= (1 << off); +} + #endif