ubd_kern.c
来自「h内核」· C语言 代码 · 共 1,656 行 · 第 1/3 页
C
1,656 行
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 = os_write_file(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 __user *loc = (struct hd_geometry __user *) 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 __user *) arg, (char *) &ubd_id, sizeof(ubd_id))) return(-EFAULT); return(0); case CDROMVOLREAD: if(copy_from_user(&volume, (char __user *) arg, sizeof(volume))) return(-EFAULT); volume.channel0 = 255; volume.channel1 = 255; volume.channel2 = 255; volume.channel3 = 255; if(copy_to_user((char __user *) 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);static int same_backing_files(char *from_cmdline, char *from_cow, char *cow){ struct uml_stat buf1, buf2; int err; if(from_cmdline == NULL) return(1); if(!strcmp(from_cmdline, from_cow)) return(1); err = os_stat_file(from_cmdline, &buf1); if(err < 0){ printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err); return(1); } err = os_stat_file(from_cow, &buf2); if(err < 0){ printk("Couldn't stat '%s', err = %d\n", from_cow, -err); return(1); } if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino)) return(1); printk("Backing file mismatch - \"%s\" requested,\n" "\"%s\" specified in COW header of \"%s\"\n", from_cmdline, from_cow, cow); return(0);}static int backing_file_mismatch(char *file, __u64 size, time_t mtime){ unsigned long modtime; long long actual; int err; err = os_file_modtime(file, &modtime); if(err < 0){ printk("Failed to get modification time of backing file " "\"%s\", err = %d\n", file, -err); return(err); } err = os_file_size(file, &actual); if(err < 0){ printk("Failed to get size of backing file \"%s\", " "err = %d\n", file, -err); return(err); } if(actual != size){ /*__u64 can be a long on AMD64 and with %lu GCC complains; so * the typecast.*/ printk("Size mismatch (%llu vs %llu) of COW header vs backing " "file\n", (unsigned long long) size, actual); return(-EINVAL); } if(modtime != mtime){ printk("mtime mismatch (%ld vs %ld) of COW header vs backing " "file\n", mtime, modtime); return(-EINVAL); } return(0);}int read_cow_bitmap(int fd, void *buf, int offset, int len){ int err; err = os_seek_file(fd, offset); if(err < 0) return(err); err = os_read_file(fd, buf, len); if(err < 0) return(err); return(0);}int open_ubd_file(char *file, struct openflags *openflags, char **backing_file_out, int *bitmap_offset_out, unsigned long *bitmap_len_out, int *data_offset_out, int *create_cow_out){ time_t mtime; unsigned long long size; __u32 version, align; char *backing_file; int fd, err, sectorsize, same, mode = 0644; fd = os_open_file(file, *openflags, mode); if(fd < 0){ if((fd == -ENOENT) && (create_cow_out != NULL)) *create_cow_out = 1; if(!openflags->w || ((fd != -EROFS) && (fd != -EACCES))) return(fd); openflags->w = 0; fd = os_open_file(file, *openflags, mode); if(fd < 0) return(fd); } err = os_lock_file(fd, openflags->w); if(err < 0){ printk("Failed to lock '%s', err = %d\n", file, -err); goto out_close; } if(backing_file_out == NULL) return(fd); err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime, &size, §orsize, &align, bitmap_offset_out); if(err && (*backing_file_out != NULL)){ printk("Failed to read COW header from COW file \"%s\", " "errno = %d\n", file, -err); goto out_close; } if(err) return(fd); if(backing_file_out == NULL) return(fd); same = same_backing_files(*backing_file_out, backing_file, file); if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){ printk("Switching backing file to '%s'\n", *backing_file_out); err = write_cow_header(file, fd, *backing_file_out, sectorsize, align, &size); if(err){ printk("Switch failed, errno = %d\n", -err); return(err); } } else { *backing_file_out = backing_file; err = backing_file_mismatch(*backing_file_out, size, mtime); if(err) goto out_close; } cow_sizes(version, size, sectorsize, align, *bitmap_offset_out, bitmap_len_out, data_offset_out); return(fd); out_close: os_close_file(fd); return(err);}int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, int sectorsize, int alignment, int *bitmap_offset_out, unsigned long *bitmap_len_out, int *data_offset_out){ int err, fd; flags.c = 1; fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL); if(fd < 0){ err = fd; printk("Open of COW file '%s' failed, errno = %d\n", cow_file, -err); goto out; } err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment, bitmap_offset_out, bitmap_len_out, data_offset_out); if(!err) return(fd); os_close_file(fd); out: return(err);}static int update_bitmap(struct io_thread_req *req){ int n; if(req->cow_offset == -1) return(0); n = os_seek_file(req->fds[1], req->cow_offset); if(n < 0){ printk("do_io - bitmap lseek failed : err = %d\n", -n); return(1); } n = os_write_file(req->fds[1], &req->bitmap_words, sizeof(req->bitmap_words)); if(n != sizeof(req->bitmap_words)){ printk("do_io - bitmap update failed, err = %d fd = %d\n", -n, req->fds[1]); return(1); } return(0);}void do_io(struct io_thread_req *req){ char *buf; unsigned long len; int n, nsectors, start, end, bit; int err; __u64 off; if(req->op == UBD_MMAP){ /* Touch the page to force the host to do any necessary IO to * get it into memory */ n = *((volatile int *) req->buffer); req->error = update_bitmap(req); return; } nsectors = req->length / req->sectorsize; start = 0; do { bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask); end = start; while((end < nsectors) && (ubd_test_bit(end, (unsigned char *) &req->sector_mask) == bit)) end++; off = req->offset + req->offsets[bit] + start * req->sectorsize; len = (end - start) * req->sectorsize; buf = &req->buffer[start * req->sectorsize]; err = os_seek_file(req->fds[bit], off); if(err < 0){ printk("do_io - lseek failed : err = %d\n", -err); req->error = 1; return; } if(req->op == UBD_READ){ n = 0; do { buf = &buf[n]; len -= n; n = os_read_file(req->fds[bit], buf, len); if (n < 0) { printk("do_io - read failed, err = %d " "fd = %d\n", -n, req->fds[bit]); req->error = 1; return; } } while((n < len) && (n != 0)); if (n < len) memset(&buf[n], 0, len - n); } else { n = os_write_file(req->fds[bit], buf, len); if(n != len){ printk("do_io - write failed err = %d " "fd = %d\n", -n, req->fds[bit]); req->error = 1; return; } } start = end; } while(start < nsectors); req->error = update_bitmap(req);}/* Changed in start_io_thread, which is serialized by being called only * from ubd_init, which is an initcall. */int kernel_fd = -1;/* Only changed by the io thread */int io_count = 0;int io_thread(void *arg){ struct io_thread_req req; int n; ignore_sigwinch_sig(); while(1){ n = os_read_file(kernel_fd, &req, sizeof(req)); if(n != sizeof(req)){ if(n < 0) printk("io_thread - read failed, fd = %d, " "err = %d\n", kernel_fd, -n); else { printk("io_thread - short read, fd = %d, " "length = %d\n", kernel_fd, n); } continue; } io_count++; do_io(&req); n = os_write_file(kernel_fd, &req, sizeof(req)); if(n != sizeof(req)) printk("io_thread - write failed, fd = %d, err = %d\n", kernel_fd, -n); }}/* * 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 + -
显示快捷键?