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

📄 dm-ioctl.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 2 页
字号:
{	int r = 0;	struct mapped_device *md;	md = find_device(param);	if (!md)		return -ENXIO;	if (!dm_suspended(md))		r = dm_suspend(md);	if (!r)		r = __dev_status(md, param);	dm_put(md);	return r;}static int do_resume(struct dm_ioctl *param){	int r = 0;	struct hash_cell *hc;	struct mapped_device *md;	struct dm_table *new_map;	down_write(&_hash_lock);	hc = __find_device_hash_cell(param);	if (!hc) {		DMWARN("device doesn't appear to be in the dev hash table.");		up_write(&_hash_lock);		return -ENXIO;	}	md = hc->md;	dm_get(md);	new_map = hc->new_map;	hc->new_map = NULL;	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;	up_write(&_hash_lock);	/* Do we need to load a new map ? */	if (new_map) {		/* Suspend if it isn't already suspended */		if (!dm_suspended(md))			dm_suspend(md);		r = dm_swap_table(md, new_map);		if (r) {			dm_put(md);			dm_table_put(new_map);			return r;		}		if (dm_table_get_mode(new_map) & FMODE_WRITE)			set_disk_ro(dm_disk(md), 0);		else			set_disk_ro(dm_disk(md), 1);		dm_table_put(new_map);	}	if (dm_suspended(md))		r = dm_resume(md);	if (!r)		r = __dev_status(md, param);	dm_put(md);	return r;}/* * Set or unset the suspension state of a device. * If the device already is in the requested state we just return its status. */static int dev_suspend(struct dm_ioctl *param, size_t param_size){	if (param->flags & DM_SUSPEND_FLAG)		return do_suspend(param);	return do_resume(param);}/* * Copies device info back to user space, used by * the create and info ioctls. */static int dev_status(struct dm_ioctl *param, size_t param_size){	int r;	struct mapped_device *md;	md = find_device(param);	if (!md)		return -ENXIO;	r = __dev_status(md, param);	dm_put(md);	return r;}/* * Build up the status struct for each target */static void retrieve_status(struct dm_table *table,			    struct dm_ioctl *param, size_t param_size){	unsigned int i, num_targets;	struct dm_target_spec *spec;	char *outbuf, *outptr;	status_type_t type;	size_t remaining, len, used = 0;	outptr = outbuf = get_result_buffer(param, param_size, &len);	if (param->flags & DM_STATUS_TABLE_FLAG)		type = STATUSTYPE_TABLE;	else		type = STATUSTYPE_INFO;	/* Get all the target info */	num_targets = dm_table_get_num_targets(table);	for (i = 0; i < num_targets; i++) {		struct dm_target *ti = dm_table_get_target(table, i);		remaining = len - (outptr - outbuf);		if (remaining <= sizeof(struct dm_target_spec)) {			param->flags |= DM_BUFFER_FULL_FLAG;			break;		}		spec = (struct dm_target_spec *) outptr;		spec->status = 0;		spec->sector_start = ti->begin;		spec->length = ti->len;		strncpy(spec->target_type, ti->type->name,			sizeof(spec->target_type));		outptr += sizeof(struct dm_target_spec);		remaining = len - (outptr - outbuf);		if (remaining <= 0) {			param->flags |= DM_BUFFER_FULL_FLAG;			break;		}		/* Get the status/table string from the target driver */		if (ti->type->status) {			if (ti->type->status(ti, type, outptr, remaining)) {				param->flags |= DM_BUFFER_FULL_FLAG;				break;			}		} else			outptr[0] = '\0';		outptr += strlen(outptr) + 1;		used = param->data_start + (outptr - outbuf);		outptr = align_ptr(outptr);		spec->next = outptr - outbuf;	}	if (used)		param->data_size = used;	param->target_count = num_targets;}/* * Wait for a device to report an event */static int dev_wait(struct dm_ioctl *param, size_t param_size){	int r;	struct mapped_device *md;	struct dm_table *table;	md = find_device(param);	if (!md)		return -ENXIO;	/*	 * Wait for a notification event	 */	if (dm_wait_event(md, param->event_nr)) {		r = -ERESTARTSYS;		goto out;	}	/*	 * The userland program is going to want to know what	 * changed to trigger the event, so we may as well tell	 * him and save an ioctl.	 */	r = __dev_status(md, param);	if (r)		goto out;	table = dm_get_table(md);	if (table) {		retrieve_status(table, param, param_size);		dm_table_put(table);	} out:	dm_put(md);	return r;}static inline int get_mode(struct dm_ioctl *param){	int mode = FMODE_READ | FMODE_WRITE;	if (param->flags & DM_READONLY_FLAG)		mode = FMODE_READ;	return mode;}static int next_target(struct dm_target_spec *last, uint32_t next, void *end,		       struct dm_target_spec **spec, char **target_params){	*spec = (struct dm_target_spec *) ((unsigned char *) last + next);	*target_params = (char *) (*spec + 1);	if (*spec < (last + 1))		return -EINVAL;	return invalid_str(*target_params, end);}static int populate_table(struct dm_table *table,			  struct dm_ioctl *param, size_t param_size){	int r;	unsigned int i = 0;	struct dm_target_spec *spec = (struct dm_target_spec *) param;	uint32_t next = param->data_start;	void *end = (void *) param + param_size;	char *target_params;	if (!param->target_count) {		DMWARN("populate_table: no targets specified");		return -EINVAL;	}	for (i = 0; i < param->target_count; i++) {		r = next_target(spec, next, end, &spec, &target_params);		if (r) {			DMWARN("unable to find target");			return r;		}		r = dm_table_add_target(table, spec->target_type,					(sector_t) spec->sector_start,					(sector_t) spec->length,					target_params);		if (r) {			DMWARN("error adding target to table");			return r;		}		next = spec->next;	}	return dm_table_complete(table);}static int table_load(struct dm_ioctl *param, size_t param_size){	int r;	struct hash_cell *hc;	struct dm_table *t;	r = dm_table_create(&t, get_mode(param), param->target_count);	if (r)		return r;	r = populate_table(t, param, param_size);	if (r) {		dm_table_put(t);		return r;	}	down_write(&_hash_lock);	hc = __find_device_hash_cell(param);	if (!hc) {		DMWARN("device doesn't appear to be in the dev hash table.");		up_write(&_hash_lock);		return -ENXIO;	}	if (hc->new_map)		dm_table_put(hc->new_map);	hc->new_map = t;	param->flags |= DM_INACTIVE_PRESENT_FLAG;	r = __dev_status(hc->md, param);	up_write(&_hash_lock);	return r;}static int table_clear(struct dm_ioctl *param, size_t param_size){	int r;	struct hash_cell *hc;	down_write(&_hash_lock);	hc = __find_device_hash_cell(param);	if (!hc) {		DMWARN("device doesn't appear to be in the dev hash table.");		up_write(&_hash_lock);		return -ENXIO;	}	if (hc->new_map) {		dm_table_put(hc->new_map);		hc->new_map = NULL;	}	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;	r = __dev_status(hc->md, param);	up_write(&_hash_lock);	return r;}/* * Retrieves a list of devices used by a particular dm device. */static void retrieve_deps(struct dm_table *table,			  struct dm_ioctl *param, size_t param_size){	unsigned int count = 0;	struct list_head *tmp;	size_t len, needed;	struct dm_dev *dd;	struct dm_target_deps *deps;	deps = get_result_buffer(param, param_size, &len);	/*	 * Count the devices.	 */	list_for_each (tmp, dm_table_get_devices(table))		count++;	/*	 * Check we have enough space.	 */	needed = sizeof(*deps) + (sizeof(*deps->dev) * count);	if (len < needed) {		param->flags |= DM_BUFFER_FULL_FLAG;		return;	}	/*	 * Fill in the devices.	 */	deps->count = count;	count = 0;	list_for_each_entry (dd, dm_table_get_devices(table), list)		deps->dev[count++] = huge_encode_dev(dd->bdev->bd_dev);	param->data_size = param->data_start + needed;}static int table_deps(struct dm_ioctl *param, size_t param_size){	int r = 0;	struct mapped_device *md;	struct dm_table *table;	md = find_device(param);	if (!md)		return -ENXIO;	r = __dev_status(md, param);	if (r)		goto out;	table = dm_get_table(md);	if (table) {		retrieve_deps(table, param, param_size);		dm_table_put(table);	} out:	dm_put(md);	return r;}/* * Return the status of a device as a text string for each * target. */static int table_status(struct dm_ioctl *param, size_t param_size){	int r;	struct mapped_device *md;	struct dm_table *table;	md = find_device(param);	if (!md)		return -ENXIO;	r = __dev_status(md, param);	if (r)		goto out;	table = dm_get_table(md);	if (table) {		retrieve_status(table, param, param_size);		dm_table_put(table);	} out:	dm_put(md);	return r;}/*----------------------------------------------------------------- * Implementation of open/close/ioctl on the special char * device. *---------------------------------------------------------------*/static ioctl_fn lookup_ioctl(unsigned int cmd){	static struct {		int cmd;		ioctl_fn fn;	} _ioctls[] = {		{DM_VERSION_CMD, NULL},	/* version is dealt with elsewhere */		{DM_REMOVE_ALL_CMD, remove_all},		{DM_LIST_DEVICES_CMD, list_devices},		{DM_DEV_CREATE_CMD, dev_create},		{DM_DEV_REMOVE_CMD, dev_remove},		{DM_DEV_RENAME_CMD, dev_rename},		{DM_DEV_SUSPEND_CMD, dev_suspend},		{DM_DEV_STATUS_CMD, dev_status},		{DM_DEV_WAIT_CMD, dev_wait},		{DM_TABLE_LOAD_CMD, table_load},		{DM_TABLE_CLEAR_CMD, table_clear},		{DM_TABLE_DEPS_CMD, table_deps},		{DM_TABLE_STATUS_CMD, table_status},		{DM_LIST_VERSIONS_CMD, list_versions}	};	return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;}/* * As well as checking the version compatibility this always * copies the kernel interface version out. */static int check_version(unsigned int cmd, struct dm_ioctl __user *user){	uint32_t version[3];	int r = 0;	if (copy_from_user(version, user->version, sizeof(version)))		return -EFAULT;	if ((DM_VERSION_MAJOR != version[0]) ||	    (DM_VERSION_MINOR < version[1])) {		DMWARN("ioctl interface mismatch: "		       "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",		       DM_VERSION_MAJOR, DM_VERSION_MINOR,		       DM_VERSION_PATCHLEVEL,		       version[0], version[1], version[2], cmd);		r = -EINVAL;	}	/*	 * Fill in the kernel version.	 */	version[0] = DM_VERSION_MAJOR;	version[1] = DM_VERSION_MINOR;	version[2] = DM_VERSION_PATCHLEVEL;	if (copy_to_user(user->version, version, sizeof(version)))		return -EFAULT;	return r;}static void free_params(struct dm_ioctl *param){	vfree(param);}static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param){	struct dm_ioctl tmp, *dmi;	if (copy_from_user(&tmp, user, sizeof(tmp)))		return -EFAULT;	if (tmp.data_size < sizeof(tmp))		return -EINVAL;	dmi = (struct dm_ioctl *) vmalloc(tmp.data_size);	if (!dmi)		return -ENOMEM;	if (copy_from_user(dmi, user, tmp.data_size)) {		vfree(dmi);		return -EFAULT;	}	*param = dmi;	return 0;}static int validate_params(uint cmd, struct dm_ioctl *param){	/* Always clear this flag */	param->flags &= ~DM_BUFFER_FULL_FLAG;	/* Ignores parameters */	if (cmd == DM_REMOVE_ALL_CMD ||	    cmd == DM_LIST_DEVICES_CMD ||	    cmd == DM_LIST_VERSIONS_CMD)		return 0;	/* Unless creating, either name or uuid but not both */	if (cmd != DM_DEV_CREATE_CMD) {		if ((!*param->uuid && !*param->name) ||		    (*param->uuid && *param->name)) {			DMWARN("one of name or uuid must be supplied, cmd(%u)",			       cmd);			return -EINVAL;		}	}	/* Ensure strings are terminated */	param->name[DM_NAME_LEN - 1] = '\0';	param->uuid[DM_UUID_LEN - 1] = '\0';	return 0;}static int ctl_ioctl(struct inode *inode, struct file *file,		     uint command, ulong u){	int r = 0;	unsigned int cmd;	struct dm_ioctl *param;	struct dm_ioctl __user *user = (struct dm_ioctl __user *) u;	ioctl_fn fn = NULL;	size_t param_size;	/* only root can play with this */	if (!capable(CAP_SYS_ADMIN))		return -EACCES;	if (_IOC_TYPE(command) != DM_IOCTL)		return -ENOTTY;	cmd = _IOC_NR(command);	/*	 * Check the interface version passed in.  This also	 * writes out the kernel's interface version.	 */	r = check_version(cmd, user);	if (r)		return r;	/*	 * Nothing more to do for the version command.	 */	if (cmd == DM_VERSION_CMD)		return 0;	fn = lookup_ioctl(cmd);	if (!fn) {		DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);		return -ENOTTY;	}	/*	 * Trying to avoid low memory issues when a device is	 * suspended.	 */	current->flags |= PF_MEMALLOC;	/*	 * Copy the parameters into kernel space.	 */	r = copy_params(user, &param);	if (r) {		current->flags &= ~PF_MEMALLOC;		return r;	}	/*	 * FIXME: eventually we will remove the PF_MEMALLOC flag	 * here.  However the tools still do nasty things like	 * 'load' while a device is suspended.	 */	r = validate_params(cmd, param);	if (r)		goto out;	param_size = param->data_size;	param->data_size = sizeof(*param);	r = fn(param, param_size);	/*	 * Copy the results back to userland.	 */	if (!r && copy_to_user(user, param, param->data_size))		r = -EFAULT; out:	free_params(param);	current->flags &= ~PF_MEMALLOC;	return r;}static struct file_operations _ctl_fops = {	.ioctl	 = ctl_ioctl,	.owner	 = THIS_MODULE,};static struct miscdevice _dm_misc = {	.minor 		= MISC_DYNAMIC_MINOR,	.name  		= DM_NAME,	.devfs_name 	= "mapper/control",	.fops  		= &_ctl_fops};/* * Create misc character device and link to DM_DIR/control. */int __init dm_interface_init(void){	int r;	r = dm_hash_init();	if (r)		return r;	r = misc_register(&_dm_misc);	if (r) {		DMERR("misc_register failed for control device");		dm_hash_exit();		return r;	}	DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,	       DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,	       DM_DRIVER_EMAIL);	return 0;}void dm_interface_exit(void){	if (misc_deregister(&_dm_misc) < 0)		DMERR("misc_deregister failed for control device");	dm_hash_exit();}

⌨️ 快捷键说明

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