ubd_kern.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,258 行 · 第 1/2 页
C
1,258 行
int n, err; str = uml_strdup(str); if(str == NULL){ printk(KERN_ERR "ubd_config failed to strdup string\n"); return(1); } err = ubd_setup_common(str, &n); if(err){ kfree(str); return(-1); } if(n == -1) return(0); spin_lock(&ubd_lock); err = ubd_add(n); if(err) ubd_dev[n].file = NULL; spin_unlock(&ubd_lock); return(err);}static int ubd_get_config(char *name, char *str, int size, char **error_out){ struct ubd *dev; char *end; int n, len = 0; n = simple_strtoul(name, &end, 0); if((*end != '\0') || (end == name)){ *error_out = "ubd_get_config : didn't parse device number"; return(-1); } if((n >= MAX_DEV) || (n < 0)){ *error_out = "ubd_get_config : device number out of range"; return(-1); } dev = &ubd_dev[n]; spin_lock(&ubd_lock); if(dev->file == NULL){ CONFIG_CHUNK(str, size, len, "", 1); goto out; } CONFIG_CHUNK(str, size, len, dev->file, 0); if(dev->cow.file != NULL){ CONFIG_CHUNK(str, size, len, ",", 0); CONFIG_CHUNK(str, size, len, dev->cow.file, 1); } else CONFIG_CHUNK(str, size, len, "", 1); out: spin_unlock(&ubd_lock); return(len);}static int ubd_remove(char *str){ struct ubd *dev; int n, err = -ENODEV; n = parse_unit(&str); if((n < 0) || (n >= MAX_DEV)) return(err); dev = &ubd_dev[n]; if(dev->count > 0) return(-EBUSY); /* you cannot remove a open disk */ err = 0; spin_lock(&ubd_lock); if(ubd_gendisk[n] == NULL) goto out; del_gendisk(ubd_gendisk[n]); put_disk(ubd_gendisk[n]); ubd_gendisk[n] = NULL; if(fake_gendisk[n] != NULL){ del_gendisk(fake_gendisk[n]); put_disk(fake_gendisk[n]); fake_gendisk[n] = NULL; } *dev = ((struct ubd) DEFAULT_UBD); err = 0; out: spin_unlock(&ubd_lock); return(err);}static struct mc_device ubd_mc = { .name = "ubd", .config = ubd_config, .get_config = ubd_get_config, .remove = ubd_remove,};static int ubd_mc_init(void){ mconsole_register_dev(&ubd_mc); return 0;}__initcall(ubd_mc_init);int ubd_init(void){ int i; devfs_mk_dir("ubd"); if (register_blkdev(MAJOR_NR, "ubd")) return -1; ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock); if (!ubd_queue) { unregister_blkdev(MAJOR_NR, "ubd"); return -1; } if (fake_major != MAJOR_NR) { char name[sizeof("ubd_nnn\0")]; snprintf(name, sizeof(name), "ubd_%d", fake_major); devfs_mk_dir(name); if (register_blkdev(fake_major, "ubd")) return -1; } for (i = 0; i < MAX_DEV; i++) ubd_add(i); return 0;}late_initcall(ubd_init);int ubd_driver_init(void){ unsigned long stack; int err; /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/ if(global_openflags.s){ printk(KERN_INFO "ubd: Synchronous mode\n"); /* Letting ubd=sync be like using ubd#s= instead of ubd#= is * enough. So use anyway the io thread. */ } stack = alloc_stack(0, 0); io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), &thread_fd); if(io_pid < 0){ printk(KERN_ERR "ubd : Failed to start I/O thread (errno = %d) - " "falling back to synchronous I/O\n", -io_pid); io_pid = -1; return(0); } err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr, SA_INTERRUPT, "ubd", ubd_dev); if(err != 0) printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); return(err);}device_initcall(ubd_driver_init);static int ubd_open(struct inode *inode, struct file *filp){ struct gendisk *disk = inode->i_bdev->bd_disk; struct ubd *dev = disk->private_data; int err = 0; if(dev->count == 0){ err = ubd_open_dev(dev); if(err){ printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n", disk->disk_name, dev->file, -err); goto out; } } dev->count++; if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){ if(--dev->count == 0) ubd_close(dev); err = -EROFS; } out: return(err);}static int ubd_release(struct inode * inode, struct file * file){ struct gendisk *disk = inode->i_bdev->bd_disk; struct ubd *dev = disk->private_data; if(--dev->count == 0) ubd_close(dev); return(0);}static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask, __u64 *cow_offset, unsigned long *bitmap, __u64 bitmap_offset, unsigned long *bitmap_words, __u64 bitmap_len){ __u64 sector = io_offset >> 9; int i, update_bitmap = 0; for(i = 0; i < length >> 9; i++){ if(cow_mask != NULL) ubd_set_bit(i, (unsigned char *) cow_mask); if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) continue; update_bitmap = 1; ubd_set_bit(sector + i, (unsigned char *) bitmap); } if(!update_bitmap) return; *cow_offset = sector / (sizeof(unsigned long) * 8); /* This takes care of the case where we're exactly at the end of the * device, and *cow_offset + 1 is off the end. So, just back it up * by one word. Thanks to Lynn Kerby for the fix and James McMechan * for the original diagnosis. */ if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) / sizeof(unsigned long) - 1)) (*cow_offset)--; bitmap_words[0] = bitmap[*cow_offset]; bitmap_words[1] = bitmap[*cow_offset + 1]; *cow_offset *= sizeof(unsigned long); *cow_offset += bitmap_offset;}static void cowify_req(struct io_thread_req *req, unsigned long *bitmap, __u64 bitmap_offset, __u64 bitmap_len){ __u64 sector = req->offset >> 9; int i; if(req->length > (sizeof(req->sector_mask) * 8) << 9) panic("Operation too long"); if(req->op == UBD_READ) { for(i = 0; i < req->length >> 9; i++){ if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) ubd_set_bit(i, (unsigned char *) &req->sector_mask); } } else cowify_bitmap(req->offset, req->length, &req->sector_mask, &req->cow_offset, bitmap, bitmap_offset, req->bitmap_words, bitmap_len);}static int mmap_fd(struct request *req, struct ubd *dev, __u64 offset){ __u64 sector; unsigned char *bitmap; int bit, i; /* mmap must have been requested on the command line */ if(!ubd_do_mmap) return(-1); /* The buffer must be page aligned */ if(((unsigned long) req->buffer % UBD_MMAP_BLOCK_SIZE) != 0) return(-1); /* The request must be a page long */ if((req->current_nr_sectors << 9) != PAGE_SIZE) return(-1); if(dev->cow.file == NULL) return(dev->fd); sector = offset >> 9; bitmap = (unsigned char *) dev->cow.bitmap; bit = ubd_test_bit(sector, bitmap); for(i = 1; i < req->current_nr_sectors; i++){ if(ubd_test_bit(sector + i, bitmap) != bit) return(-1); } if(bit || (rq_data_dir(req) == WRITE)) offset += dev->cow.data_offset; /* The data on disk must be page aligned */ if((offset % UBD_MMAP_BLOCK_SIZE) != 0) return(-1); return(bit ? dev->fd : dev->cow.fd);}static int prepare_mmap_request(struct ubd *dev, int fd, __u64 offset, struct request *req, struct io_thread_req *io_req){ int err; if(rq_data_dir(req) == WRITE){ /* Writes are almost no-ops since the new data is already in the * host page cache */ dev->map_writes++; if(dev->cow.file != NULL) cowify_bitmap(io_req->offset, io_req->length, &io_req->sector_mask, &io_req->cow_offset, dev->cow.bitmap, dev->cow.bitmap_offset, io_req->bitmap_words, dev->cow.bitmap_len); } else { int w; if((dev->cow.file != NULL) && (fd == dev->cow.fd)) w = 0; else w = dev->openflags.w; if((dev->cow.file != NULL) && (fd == dev->fd)) offset += dev->cow.data_offset; err = physmem_subst_mapping(req->buffer, fd, offset, w); if(err){ printk("physmem_subst_mapping failed, err = %d\n", -err); return(1); } dev->map_reads++; } io_req->op = UBD_MMAP; io_req->buffer = req->buffer; return(0);}/* Called with ubd_io_lock held */static int prepare_request(struct request *req, struct io_thread_req *io_req){ struct gendisk *disk = req->rq_disk; struct ubd *dev = disk->private_data; __u64 offset; int len, fd; if(req->rq_status == RQ_INACTIVE) return(1); if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ printk("Write attempted on readonly ubd device %s\n", disk->disk_name); end_request(req, 0); return(1); } offset = ((__u64) req->sector) << 9; len = req->current_nr_sectors << 9; io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd; io_req->fds[1] = dev->fd; io_req->map_fd = -1; io_req->cow_offset = -1; io_req->offset = offset; io_req->length = len; io_req->error = 0; io_req->sector_mask = 0; fd = mmap_fd(req, dev, io_req->offset); if(fd > 0){ /* If mmapping is otherwise OK, but the first access to the * page is a write, then it's not mapped in yet. So we have * to write the data to disk first, then we can map the disk * page in and continue normally from there. */ if((rq_data_dir(req) == WRITE) && !is_remapped(req->buffer)){ io_req->map_fd = dev->fd; io_req->map_offset = io_req->offset + dev->cow.data_offset; dev->write_maps++; } else return(prepare_mmap_request(dev, fd, io_req->offset, req, io_req)); } if(rq_data_dir(req) == READ) dev->nomap_reads++; else dev->nomap_writes++; io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE; io_req->offsets[0] = 0; io_req->offsets[1] = dev->cow.data_offset; io_req->buffer = req->buffer; io_req->sectorsize = 1 << 9; if(dev->cow.file != NULL) cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset, dev->cow.bitmap_len); return(0);}/* Called with ubd_io_lock held */static void do_ubd_request(request_queue_t *q){ struct io_thread_req io_req; struct request *req; int err, n; if(thread_fd == -1){ while((req = elv_next_request(q)) != NULL){ err = prepare_request(req, &io_req); if(!err){ do_io(&io_req); __ubd_finish(req, io_req.error); } } } else { if(do_ubd || (req = elv_next_request(q)) == NULL) return; err = prepare_request(req, &io_req); if(!err){ do_ubd = ubd_handler; n = write_ubd_fs(thread_fd, (char *) &io_req, sizeof(io_req)); if(n != sizeof(io_req)) printk("write to io thread failed, " "errno = %d\n", -n); } }}static int ubd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ struct hd_geometry *loc = (struct hd_geometry *) arg; struct ubd *dev = inode->i_bdev->bd_disk->private_data; struct hd_driveid ubd_id = { .cyls = 0, .heads = 128, .sectors = 32, }; switch (cmd) { struct hd_geometry g; struct cdrom_volctrl volume; case HDIO_GETGEO: if(!loc) return(-EINVAL); g.heads = 128; g.sectors = 32; g.cylinders = dev->size / (128 * 32 * 512); g.start = get_start_sect(inode->i_bdev); return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0); case HDIO_GET_IDENTITY: ubd_id.cyls = dev->size / (128 * 32 * 512); if(copy_to_user((char *) arg, (char *) &ubd_id, sizeof(ubd_id))) return(-EFAULT); return(0); case CDROMVOLREAD: if(copy_from_user(&volume, (char *) arg, sizeof(volume))) return(-EFAULT); volume.channel0 = 255; volume.channel1 = 255; volume.channel2 = 255; volume.channel3 = 255; if(copy_to_user((char *) arg, &volume, sizeof(volume))) return(-EFAULT); return(0); } return(-EINVAL);}static int ubd_check_remapped(int fd, unsigned long address, int is_write, __u64 offset){ __u64 bitmap_offset; unsigned long new_bitmap[2]; int i, err, n; /* If it's not a write access, we can't do anything about it */ if(!is_write) return(0); /* We have a write */ for(i = 0; i < sizeof(ubd_dev) / sizeof(ubd_dev[0]); i++){ struct ubd *dev = &ubd_dev[i]; if((dev->fd != fd) && (dev->cow.fd != fd)) continue; /* It's a write to a ubd device */ if(!dev->openflags.w){ /* It's a write access on a read-only device - probably * shouldn't happen. If the kernel is trying to change * something with no intention of writing it back out, * then this message will clue us in that this needs * fixing */ printk("Write access to mapped page from readonly ubd " "device %d\n", i); return(0); } /* It's a write to a writeable ubd device - it must be COWed * because, otherwise, the page would have been mapped in * writeable */ if(!dev->cow.file) panic("Write fault on writeable non-COW ubd device %d", i); /* It should also be an access to the backing file since the * COW pages should be mapped in read-write */ if(fd == dev->fd) panic("Write fault on a backing page of ubd " "device %d\n", i); /* So, we do the write, copying the backing data to the COW * file... */ err = os_seek_file(dev->fd, offset + dev->cow.data_offset); if(err < 0) panic("Couldn't seek to %lld in COW file of ubd " "device %d, err = %d", offset + dev->cow.data_offset, i, -err); n = os_write_file(dev->fd, (void *) address, PAGE_SIZE); if(n != PAGE_SIZE) panic("Couldn't copy data to COW file of ubd " "device %d, err = %d", i, -n); /* ... updating the COW bitmap... */ cowify_bitmap(offset, PAGE_SIZE, NULL, &bitmap_offset, dev->cow.bitmap, dev->cow.bitmap_offset, new_bitmap, dev->cow.bitmap_len); err = os_seek_file(dev->fd, bitmap_offset); if(err < 0) panic("Couldn't seek to %lld in COW file of ubd " "device %d, err = %d", bitmap_offset, i, -err); n = os_write_file(dev->fd, new_bitmap, sizeof(new_bitmap)); if(n != sizeof(new_bitmap)) panic("Couldn't update bitmap of ubd device %d, " "err = %d", i, -n); /* Maybe we can map the COW page in, and maybe we can't. If * it is a pre-V3 COW file, we can't, since the alignment will * be wrong. If it is a V3 or later COW file which has been * moved to a system with a larger page size, then maybe we * can't, depending on the exact location of the page. */ offset += dev->cow.data_offset; /* Remove the remapping, putting the original anonymous page * back. If the COW file can be mapped in, that is done. * Otherwise, the COW page is read in. */ if(!physmem_remove_mapping((void *) address)) panic("Address 0x%lx not remapped by ubd device %d", address, i); if((offset % UBD_MMAP_BLOCK_SIZE) == 0) physmem_subst_mapping((void *) address, dev->fd, offset, 1); else { err = os_seek_file(dev->fd, offset); if(err < 0) panic("Couldn't seek to %lld in COW file of " "ubd device %d, err = %d", offset, i, -err); n = os_read_file(dev->fd, (void *) address, PAGE_SIZE); if(n != PAGE_SIZE) panic("Failed to read page from offset %llx of " "COW file of ubd device %d, err = %d", offset, i, -n); } return(1); } /* It's not a write on a ubd device */ return(0);}static struct remapper ubd_remapper = { .list = LIST_HEAD_INIT(ubd_remapper.list), .proc = ubd_check_remapped,};static int ubd_remapper_setup(void){ if(ubd_do_mmap) register_remapper(&ubd_remapper); return(0);}__initcall(ubd_remapper_setup);/* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-file-style: "linux" * End: */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?