dcssblk.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 700 行 · 第 1/2 页

C
700
字号
/* * dcssblk.c -- the S/390 block driver for dcss memory * * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer */#include <linux/module.h>#include <linux/ctype.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/blkdev.h>#include <asm/extmem.h>#include <asm/io.h>#include <linux/completion.h>#include <linux/interrupt.h>#include <asm/ccwdev.h> 	// for s390_root_dev_(un)register()//#define DCSSBLK_DEBUG		/* Debug messages on/off */#define DCSSBLK_NAME "dcssblk"#define DCSSBLK_MINORS_PER_DISK 1#ifdef DCSSBLK_DEBUG#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x)#else#define PRINT_DEBUG(x...) do {} while (0)#endif#define PRINT_INFO(x...)  printk(KERN_INFO DCSSBLK_NAME " info: " x)#define PRINT_WARN(x...)  printk(KERN_WARNING DCSSBLK_NAME " warning: " x)#define PRINT_ERR(x...)	  printk(KERN_ERR DCSSBLK_NAME " error: " x)static int dcssblk_open(struct inode *inode, struct file *filp);static int dcssblk_release(struct inode *inode, struct file *filp);static int dcssblk_make_request(struct request_queue *q, struct bio *bio);static int dcssblk_major;static struct block_device_operations dcssblk_devops = {	.owner   = THIS_MODULE,	.open    = dcssblk_open,	.release = dcssblk_release,};static ssize_t dcssblk_add_store(struct device * dev, const char * buf,				  size_t count);static ssize_t dcssblk_remove_store(struct device * dev, const char * buf,				  size_t count);static ssize_t dcssblk_save_store(struct device * dev, const char * buf,				  size_t count);static ssize_t dcssblk_save_show(struct device *dev, char *buf);static ssize_t dcssblk_shared_store(struct device * dev, const char * buf,				  size_t count);static ssize_t dcssblk_shared_show(struct device *dev, char *buf);static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show,		   dcssblk_save_store);static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show,		   dcssblk_shared_store);static struct device *dcssblk_root_dev;struct dcssblk_dev_info {	struct list_head lh;	struct device dev;	char segment_name[BUS_ID_SIZE];	atomic_t use_count;	struct gendisk *gd;	unsigned long start;	unsigned long end;	int segment_type;	unsigned char save_pending;	unsigned char is_shared;	struct request_queue *dcssblk_queue;};static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);static struct rw_semaphore dcssblk_devices_sem;/* * release function for segment device. */static voiddcssblk_release_segment(struct device *dev){	PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id);	kfree(container_of(dev, struct dcssblk_dev_info, dev));	module_put(THIS_MODULE);}/* * get a minor number. needs to be called with * down_write(&dcssblk_devices_sem) and the * device needs to be enqueued before the semaphore is * freed. */static inline intdcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info){	int minor, found;	struct dcssblk_dev_info *entry;	if (dev_info == NULL)		return -EINVAL;	for (minor = 0; minor < (1<<MINORBITS); minor++) {		found = 0;		// test if minor available		list_for_each_entry(entry, &dcssblk_devices, lh)			if (minor == entry->gd->first_minor)				found++;		if (!found) break; // got unused minor	}	if (found)		return -EBUSY;	dev_info->gd->first_minor = minor;	return 0;}/* * get the struct dcssblk_dev_info from dcssblk_devices * for the given name. * down_read(&dcssblk_devices_sem) must be held. */static struct dcssblk_dev_info *dcssblk_get_device_by_name(char *name){	struct dcssblk_dev_info *entry;	list_for_each_entry(entry, &dcssblk_devices, lh) {		if (!strcmp(name, entry->segment_name)) {			return entry;		}	}	return NULL;}/* * device attribute for switching shared/nonshared (exclusive) * operation (show + store) */static ssize_tdcssblk_shared_show(struct device *dev, char *buf){	struct dcssblk_dev_info *dev_info;	dev_info = container_of(dev, struct dcssblk_dev_info, dev);	return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n");}static ssize_tdcssblk_shared_store(struct device *dev, const char *inbuf, size_t count){	struct dcssblk_dev_info *dev_info;	int rc;	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {		PRINT_WARN("Invalid value, must be 0 or 1\n");		return -EINVAL;	}	down_write(&dcssblk_devices_sem);	dev_info = container_of(dev, struct dcssblk_dev_info, dev);	if (atomic_read(&dev_info->use_count)) {		PRINT_ERR("share: segment %s is busy!\n",			  dev_info->segment_name);		up_write(&dcssblk_devices_sem);		return -EBUSY;	}	if ((inbuf[0] == '1') && (dev_info->is_shared == 1)) {		PRINT_WARN("Segment %s already loaded in shared mode!\n",			   dev_info->segment_name);		up_write(&dcssblk_devices_sem);		return count;	}	if ((inbuf[0] == '0') && (dev_info->is_shared == 0)) {		PRINT_WARN("Segment %s already loaded in exclusive mode!\n",			   dev_info->segment_name);		up_write(&dcssblk_devices_sem);		return count;	}	if (inbuf[0] == '1') {		// reload segment in shared mode		segment_unload(dev_info->segment_name);		rc = segment_load(dev_info->segment_name, SEGMENT_SHARED_RO,					&dev_info->start, &dev_info->end);		if (rc < 0) {			PRINT_ERR("Segment %s not reloaded, rc=%d\n",					dev_info->segment_name, rc);			goto removeseg;		}		dev_info->is_shared = 1;		PRINT_INFO("Segment %s reloaded, shared mode.\n",			   dev_info->segment_name);	} else if (inbuf[0] == '0') {		// reload segment in exclusive mode		segment_unload(dev_info->segment_name);		rc = segment_load(dev_info->segment_name, SEGMENT_EXCLUSIVE_RW,					&dev_info->start, &dev_info->end);		if (rc < 0) {			PRINT_ERR("Segment %s not reloaded, rc=%d\n",					dev_info->segment_name, rc);			goto removeseg;		}		dev_info->is_shared = 0;		PRINT_INFO("Segment %s reloaded, exclusive (read-write) mode.\n",			   dev_info->segment_name);	} else {		up_write(&dcssblk_devices_sem);		PRINT_WARN("Invalid value, must be 0 or 1\n");		return -EINVAL;	}	dev_info->segment_type = rc;	rc = count;	switch (dev_info->segment_type) {		case SEGMENT_SHARED_RO:		case SEGMENT_EXCLUSIVE_RO:			set_disk_ro(dev_info->gd, 1);			break;		case SEGMENT_SHARED_RW:		case SEGMENT_EXCLUSIVE_RW:			set_disk_ro(dev_info->gd, 0);			break;	}	if ((inbuf[0] == '1') &&	   ((dev_info->segment_type == SEGMENT_EXCLUSIVE_RO) ||	    (dev_info->segment_type == SEGMENT_EXCLUSIVE_RW))) {		PRINT_WARN("Could not get shared copy of segment %s\n",				dev_info->segment_name);		rc = -EPERM;	}	if ((inbuf[0] == '0') &&	   ((dev_info->segment_type == SEGMENT_SHARED_RO) ||	    (dev_info->segment_type == SEGMENT_SHARED_RW))) {		PRINT_WARN("Could not get exclusive copy of segment %s\n",				dev_info->segment_name);		rc = -EPERM;	}	up_write(&dcssblk_devices_sem);	goto out;removeseg:	PRINT_ERR("Could not reload segment %s, removing it now!\n",			dev_info->segment_name);	list_del(&dev_info->lh);	del_gendisk(dev_info->gd);	blk_put_queue(dev_info->dcssblk_queue);	dev_info->gd->queue = NULL;	put_disk(dev_info->gd);	device_unregister(dev);	put_device(dev);	up_write(&dcssblk_devices_sem);out:	return rc;}/* * device attribute for save operation on current copy * of the segment. If the segment is busy, saving will * become pending until it gets released, which can be * undone by storing a non-true value to this entry. * (show + store) */static ssize_tdcssblk_save_show(struct device *dev, char *buf){	struct dcssblk_dev_info *dev_info;	dev_info = container_of(dev, struct dcssblk_dev_info, dev);	return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n");}static ssize_tdcssblk_save_store(struct device *dev, const char *inbuf, size_t count){	struct dcssblk_dev_info *dev_info;	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {		PRINT_WARN("Invalid value, must be 0 or 1\n");		return -EINVAL;	}	dev_info = container_of(dev, struct dcssblk_dev_info, dev);	down_write(&dcssblk_devices_sem);	if (inbuf[0] == '1') {		if (atomic_read(&dev_info->use_count) == 0) {			// device is idle => we save immediately			PRINT_INFO("Saving segment %s\n",				   dev_info->segment_name);			segment_replace(dev_info->segment_name);		}  else {			// device is busy => we save it when it becomes			// idle in dcssblk_release			PRINT_INFO("Segment %s is currently busy, it will "				   "be saved when it becomes idle...\n",				   dev_info->segment_name);			dev_info->save_pending = 1;		}	} else if (inbuf[0] == '0') {		if (dev_info->save_pending) {			// device is busy & the user wants to undo his save			// request			dev_info->save_pending = 0;			PRINT_INFO("Pending save for segment %s deactivated\n",					dev_info->segment_name);		}	} else {		up_write(&dcssblk_devices_sem);		PRINT_WARN("Invalid value, must be 0 or 1\n");		return -EINVAL;	}	up_write(&dcssblk_devices_sem);	return count;}/* * device attribute for adding devices */static ssize_tdcssblk_add_store(struct device *dev, const char *buf, size_t count){	int rc, i;	struct dcssblk_dev_info *dev_info;	char *local_buf;	unsigned long seg_byte_size;	dev_info = NULL;	if (dev != dcssblk_root_dev) {		rc = -EINVAL;		goto out_nobuf;	}	local_buf = kmalloc(count + 1, GFP_KERNEL);	if (local_buf == NULL) {		rc = -ENOMEM;		goto out_nobuf;	}	/*	 * parse input	 */	for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {		local_buf[i] = toupper(buf[i]);	}	local_buf[i] = '\0';	if ((i == 0) || (i > 8)) {		rc = -ENAMETOOLONG;		goto out;	}	/*	 * already loaded?

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?