⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dcssblk.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * dcssblk.c -- the S/390 block driver for dcss memory * * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer */#include <linux/module.h>#include <linux/moduleparam.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#define DCSSBLK_PARM_LEN 400#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_direct_access(struct block_device *bdev, sector_t secnum,				 unsigned long *data);static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0";static int dcssblk_major;static struct block_device_operations dcssblk_devops = {	.owner   	= THIS_MODULE,	.open    	= dcssblk_open,	.release 	= dcssblk_release,	.direct_access 	= dcssblk_direct_access,};static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf,				  size_t count);static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf,				  size_t count);static ssize_t dcssblk_save_store(struct device * dev, struct device_attribute *attr, const char * buf,				  size_t count);static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf);static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf,				  size_t count);static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, 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;}/* * print appropriate error message for segment_load()/segment_type() * return code */static voiddcssblk_segment_warn(int rc, char* seg_name){	switch (rc) {	case -ENOENT:		PRINT_WARN("cannot load/query segment %s, does not exist\n",			   seg_name);		break;	case -ENOSYS:		PRINT_WARN("cannot load/query segment %s, not running on VM\n",			   seg_name);		break;	case -EIO:		PRINT_WARN("cannot load/query segment %s, hardware error\n",			   seg_name);		break;	case -ENOTSUPP:		PRINT_WARN("cannot load/query segment %s, is a multi-part "			   "segment\n", seg_name);		break;	case -ENOSPC:		PRINT_WARN("cannot load/query segment %s, overlaps with "			   "storage\n", seg_name);		break;	case -EBUSY:		PRINT_WARN("cannot load/query segment %s, overlaps with "			   "already loaded dcss\n", seg_name);		break;	case -EPERM:		PRINT_WARN("cannot load/query segment %s, already loaded in "			   "incompatible mode\n", seg_name);		break;	case -ENOMEM:		PRINT_WARN("cannot load/query segment %s, out of memory\n",			   seg_name);		break;	case -ERANGE:		PRINT_WARN("cannot load/query segment %s, exceeds kernel "			   "mapping range\n", seg_name);		break;	default:		PRINT_WARN("cannot load/query segment %s, return value %i\n",			   seg_name, rc);		break;	}}/* * device attribute for switching shared/nonshared (exclusive) * operation (show + store) */static ssize_tdcssblk_shared_show(struct device *dev, struct device_attribute *attr, 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, struct device_attribute *attr, 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);		rc = -EBUSY;		goto out;	}	if (inbuf[0] == '1') {		// reload segment in shared mode		rc = segment_modify_shared(dev_info->segment_name,					   SEGMENT_SHARED);		if (rc < 0) {			BUG_ON(rc == -EINVAL);			if (rc == -EIO || rc == -ENOENT)				goto removeseg;		} else {			dev_info->is_shared = 1;			switch (dev_info->segment_type) {				case SEG_TYPE_SR:				case SEG_TYPE_ER:				case SEG_TYPE_SC:					set_disk_ro(dev_info->gd,1);			}		}	} else if (inbuf[0] == '0') {		// reload segment in exclusive mode		if (dev_info->segment_type == SEG_TYPE_SC) {			PRINT_ERR("Segment type SC (%s) cannot be loaded in "				  "non-shared mode\n", dev_info->segment_name);			rc = -EINVAL;			goto out;		}		rc = segment_modify_shared(dev_info->segment_name,					   SEGMENT_EXCLUSIVE);		if (rc < 0) {			BUG_ON(rc == -EINVAL);			if (rc == -EIO || rc == -ENOENT)				goto removeseg;		} else {			dev_info->is_shared = 0;			set_disk_ro(dev_info->gd, 0);		}	} else {		PRINT_WARN("Invalid value, must be 0 or 1\n");		rc = -EINVAL;		goto out;	}	rc = count;	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);out:	up_write(&dcssblk_devices_sem);	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, struct device_attribute *attr, 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, struct device_attribute *attr, 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_save(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, struct device_attribute *attr, 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?	 */	down_read(&dcssblk_devices_sem);	dev_info = dcssblk_get_device_by_name(local_buf);	up_read(&dcssblk_devices_sem);	if (dev_info != NULL) {		PRINT_WARN("Segment %s already loaded!\n", local_buf);		rc = -EEXIST;		goto out;	}	/*	 * get a struct dcssblk_dev_info	 */	dev_info = kmalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);	if (dev_info == NULL) {		rc = -ENOMEM;		goto out;	}	memset(dev_info, 0, sizeof(struct dcssblk_dev_info));	strcpy(dev_info->segment_name, local_buf);	strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE);	dev_info->dev.release = dcssblk_release_segment;	INIT_LIST_HEAD(&dev_info->lh);	dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);	if (dev_info->gd == NULL) {		rc = -ENOMEM;		goto free_dev_info;

⌨️ 快捷键说明

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