ubd_kern.c
来自「linux 内核源代码」· C语言 代码 · 共 1,485 行 · 第 1/3 页
C
1,485 行
/* * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) * Licensed under the GPL *//* 2001-09-28...2002-04-17 * Partition stuff by James_McMechan@hotmail.com * old style ubd by setting UBD_SHIFT to 0 * 2002-09-27...2002-10-18 massive tinkering for 2.5 * partitions have changed in 2.5 * 2003-01-29 more tinkering for 2.5.59-1 * This should now address the sysfs problems and has * the symlink for devfs to allow for booting with * the common /dev/ubd/discX/... names rather than * only /dev/ubdN/discN this version also has lots of * clean ups preparing for ubd-many. * James McMechan */#define MAJOR_NR UBD_MAJOR#define UBD_SHIFT 4#include "linux/kernel.h"#include "linux/module.h"#include "linux/blkdev.h"#include "linux/hdreg.h"#include "linux/init.h"#include "linux/cdrom.h"#include "linux/proc_fs.h"#include "linux/ctype.h"#include "linux/capability.h"#include "linux/mm.h"#include "linux/vmalloc.h"#include "linux/blkpg.h"#include "linux/genhd.h"#include "linux/spinlock.h"#include "linux/platform_device.h"#include "linux/scatterlist.h"#include "asm/segment.h"#include "asm/uaccess.h"#include "asm/irq.h"#include "asm/types.h"#include "asm/tlbflush.h"#include "mem_user.h"#include "kern_util.h"#include "kern.h"#include "mconsole_kern.h"#include "init.h"#include "irq_user.h"#include "irq_kern.h"#include "ubd_user.h"#include "os.h"#include "mem.h"#include "mem_kern.h"#include "cow.h"enum ubd_req { UBD_READ, UBD_WRITE };struct io_thread_req { struct request *req; enum ubd_req op; int fds[2]; unsigned long offsets[2]; unsigned long long offset; unsigned long length; char *buffer; int sectorsize; unsigned long sector_mask; unsigned long long cow_offset; unsigned long bitmap_words[2]; int error;};extern int open_ubd_file(char *file, struct openflags *openflags, int shared, char **backing_file_out, int *bitmap_offset_out, unsigned long *bitmap_len_out, int *data_offset_out, int *create_cow_out);extern 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);extern int read_cow_bitmap(int fd, void *buf, int offset, int len);extern void do_io(struct io_thread_req *req);static inline int ubd_test_bit(__u64 bit, unsigned char *data){ __u64 n; int bits, off; bits = sizeof(data[0]) * 8; n = bit / bits; off = bit % bits; return (data[n] & (1 << off)) != 0;}static inline void ubd_set_bit(__u64 bit, unsigned char *data){ __u64 n; int bits, off; bits = sizeof(data[0]) * 8; n = bit / bits; off = bit % bits; data[n] |= (1 << off);}/*End stuff from ubd_user.h*/#define DRIVER_NAME "uml-blkdev"static DEFINE_MUTEX(ubd_lock);static int ubd_open(struct inode * inode, struct file * filp);static int ubd_release(struct inode * inode, struct file * file);static int ubd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg);static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);#define MAX_DEV (16)static struct block_device_operations ubd_blops = { .owner = THIS_MODULE, .open = ubd_open, .release = ubd_release, .ioctl = ubd_ioctl, .getgeo = ubd_getgeo,};/* Protected by ubd_lock */static int fake_major = MAJOR_NR;static struct gendisk *ubd_gendisk[MAX_DEV];static struct gendisk *fake_gendisk[MAX_DEV];#ifdef CONFIG_BLK_DEV_UBD_SYNC#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \ .cl = 1 })#else#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \ .cl = 1 })#endifstatic struct openflags global_openflags = OPEN_FLAGS;struct cow { /* backing file name */ char *file; /* backing file fd */ int fd; unsigned long *bitmap; unsigned long bitmap_len; int bitmap_offset; int data_offset;};#define MAX_SG 64struct ubd { struct list_head restart; /* name (and fd, below) of the file opened for writing, either the * backing or the cow file. */ char *file; int count; int fd; __u64 size; struct openflags boot_openflags; struct openflags openflags; unsigned shared:1; unsigned no_cow:1; struct cow cow; struct platform_device pdev; struct request_queue *queue; spinlock_t lock; struct scatterlist sg[MAX_SG]; struct request *request; int start_sg, end_sg;};#define DEFAULT_COW { \ .file = NULL, \ .fd = -1, \ .bitmap = NULL, \ .bitmap_offset = 0, \ .data_offset = 0, \}#define DEFAULT_UBD { \ .file = NULL, \ .count = 0, \ .fd = -1, \ .size = -1, \ .boot_openflags = OPEN_FLAGS, \ .openflags = OPEN_FLAGS, \ .no_cow = 0, \ .shared = 0, \ .cow = DEFAULT_COW, \ .lock = SPIN_LOCK_UNLOCKED, \ .request = NULL, \ .start_sg = 0, \ .end_sg = 0, \}/* Protected by ubd_lock */struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };/* Only changed by fake_ide_setup which is a setup */static int fake_ide = 0;static struct proc_dir_entry *proc_ide_root = NULL;static struct proc_dir_entry *proc_ide = NULL;static void make_proc_ide(void){ proc_ide_root = proc_mkdir("ide", NULL); proc_ide = proc_mkdir("ide0", proc_ide_root);}static int proc_ide_read_media(char *page, char **start, off_t off, int count, int *eof, void *data){ int len; strcpy(page, "disk\n"); len = strlen("disk\n"); len -= off; if (len < count){ *eof = 1; if (len <= 0) return 0; } else len = count; *start = page + off; return len;}static void make_ide_entries(char *dev_name){ struct proc_dir_entry *dir, *ent; char name[64]; if(proc_ide_root == NULL) make_proc_ide(); dir = proc_mkdir(dev_name, proc_ide); if(!dir) return; ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); if(!ent) return; ent->data = NULL; ent->read_proc = proc_ide_read_media; ent->write_proc = NULL; sprintf(name,"ide0/%s", dev_name); proc_symlink(dev_name, proc_ide_root, name);}static int fake_ide_setup(char *str){ fake_ide = 1; return 1;}__setup("fake_ide", fake_ide_setup);__uml_help(fake_ide_setup,"fake_ide\n"" Create ide0 entries that map onto ubd devices.\n\n");static int parse_unit(char **ptr){ char *str = *ptr, *end; int n = -1; if(isdigit(*str)) { n = simple_strtoul(str, &end, 0); if(end == str) return -1; *ptr = end; } else if (('a' <= *str) && (*str <= 'z')) { n = *str - 'a'; str++; *ptr = str; } return n;}/* If *index_out == -1 at exit, the passed option was a general one; * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it * should not be freed on exit. */static int ubd_setup_common(char *str, int *index_out, char **error_out){ struct ubd *ubd_dev; struct openflags flags = global_openflags; char *backing_file; int n, err = 0, i; if(index_out) *index_out = -1; n = *str; if(n == '='){ char *end; int major; str++; if(!strcmp(str, "sync")){ global_openflags = of_sync(global_openflags); goto out1; } err = -EINVAL; major = simple_strtoul(str, &end, 0); if((*end != '\0') || (end == str)){ *error_out = "Didn't parse major number"; goto out1; } mutex_lock(&ubd_lock); if(fake_major != MAJOR_NR){ *error_out = "Can't assign a fake major twice"; goto out1; } fake_major = major; printk(KERN_INFO "Setting extra ubd major number to %d\n", major); err = 0; out1: mutex_unlock(&ubd_lock); return err; } n = parse_unit(&str); if(n < 0){ *error_out = "Couldn't parse device number"; return -EINVAL; } if(n >= MAX_DEV){ *error_out = "Device number out of range"; return 1; } err = -EBUSY; mutex_lock(&ubd_lock); ubd_dev = &ubd_devs[n]; if(ubd_dev->file != NULL){ *error_out = "Device is already configured"; goto out; } if (index_out) *index_out = n; err = -EINVAL; for (i = 0; i < sizeof("rscd="); i++) { switch (*str) { case 'r': flags.w = 0; break; case 's': flags.s = 1; break; case 'd': ubd_dev->no_cow = 1; break; case 'c': ubd_dev->shared = 1; break; case '=': str++; goto break_loop; default: *error_out = "Expected '=' or flag letter " "(r, s, c, or d)"; goto out; } str++; } if (*str == '=') *error_out = "Too many flags specified"; else *error_out = "Missing '='"; goto out;break_loop: backing_file = strchr(str, ','); if (backing_file == NULL) backing_file = strchr(str, ':'); if(backing_file != NULL){ if(ubd_dev->no_cow){ *error_out = "Can't specify both 'd' and a cow file"; goto out; } else { *backing_file = '\0'; backing_file++; } } err = 0; ubd_dev->file = str; ubd_dev->cow.file = backing_file; ubd_dev->boot_openflags = flags;out: mutex_unlock(&ubd_lock); return err;}static int ubd_setup(char *str){ char *error; int err; err = ubd_setup_common(str, NULL, &error); if(err) printk(KERN_ERR "Failed to initialize device with \"%s\" : " "%s\n", str, error); return 1;}__setup("ubd", ubd_setup);__uml_help(ubd_setup,"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"" This is used to associate a device with a file in the underlying\n"" filesystem. When specifying two filenames, the first one is the\n"" COW name and the second is the backing file name. As separator you can\n"" use either a ':' or a ',': the first one allows writing things like;\n"" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"" while with a ',' the shell would not expand the 2nd '~'.\n"" When using only one filename, UML will detect whether to treat it like\n"" a COW file or a backing file. To override this detection, add the 'd'\n"" flag:\n"" ubd0d=BackingFile\n"" Usually, there is a filesystem in the file, but \n"" that's not required. Swap devices containing swap files can be\n"" specified like this. Also, a file which doesn't contain a\n"" filesystem can have its contents read in the virtual \n"" machine by running 'dd' on the device. <n> must be in the range\n"" 0 to 7. Appending an 'r' to the number will cause that device\n"" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"" an 's' will cause data to be written to disk on the host immediately.\n\n");static int udb_setup(char *str){ printk("udb%s specified on command line is almost certainly a ubd -> " "udb TYPO\n", str); return 1;}__setup("udb", udb_setup);__uml_help(udb_setup,"udb\n"" This option is here solely to catch ubd -> udb typos, which can be\n"" to impossible to catch visually unless you specifically look for\n"" them. The only result of any option starting with 'udb' is an error\n"" in the boot output.\n\n");static int fakehd_set = 0;static int fakehd(char *str){ printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n"); fakehd_set = 1; return 1;}__setup("fakehd", fakehd);__uml_help(fakehd,"fakehd\n"" Change the ubd device name to \"hd\".\n\n");static void do_ubd_request(struct request_queue * q);/* Only changed by ubd_init, which is an initcall. */int thread_fd = -1;static void ubd_end_request(struct request *req, int bytes, int uptodate){ if (!end_that_request_first(req, uptodate, bytes >> 9)) { struct ubd *dev = req->rq_disk->private_data; unsigned long flags; add_disk_randomness(req->rq_disk); spin_lock_irqsave(&dev->lock, flags); end_that_request_last(req, uptodate); spin_unlock_irqrestore(&dev->lock, flags); }}/* Callable only from interrupt context - otherwise you need to do * spin_lock_irq()/spin_lock_irqsave() */static inline void ubd_finish(struct request *req, int bytes){ if(bytes < 0){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?