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 + -
显示快捷键?