X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmd%2Fdm-ioctl.c;h=8edd6435414df900777cbc78d4bf4988d0be37e1;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=ee3c869d9701933d366dfdf18fc023eb5cef2da8;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index ee3c869d9..8edd64354 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -122,14 +123,6 @@ static struct hash_cell *__get_uuid_cell(const char *str) /*----------------------------------------------------------------- * Inserting, removing and renaming a device. *---------------------------------------------------------------*/ -static inline char *kstrdup(const char *str) -{ - char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); - if (r) - strcpy(r, str); - return r; -} - static struct hash_cell *alloc_cell(const char *name, const char *uuid, struct mapped_device *md) { @@ -139,7 +132,7 @@ static struct hash_cell *alloc_cell(const char *name, const char *uuid, if (!hc) return NULL; - hc->name = kstrdup(name); + hc->name = kstrdup(name, GFP_KERNEL); if (!hc->name) { kfree(hc); return NULL; @@ -149,7 +142,7 @@ static struct hash_cell *alloc_cell(const char *name, const char *uuid, hc->uuid = NULL; else { - hc->uuid = kstrdup(uuid); + hc->uuid = kstrdup(uuid, GFP_KERNEL); if (!hc->uuid) { kfree(hc->name); kfree(hc); @@ -238,14 +231,23 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi static void __hash_remove(struct hash_cell *hc) { + struct dm_table *table; + /* remove from the dev hash */ list_del(&hc->uuid_list); list_del(&hc->name_list); unregister_with_devfs(hc); dm_set_mdptr(hc->md, NULL); - dm_put(hc->md); + + table = dm_get_table(hc->md); + if (table) { + dm_table_event(table); + dm_table_put(table); + } + if (hc->new_map) dm_table_put(hc->new_map); + dm_put(hc->md); free_cell(hc); } @@ -269,11 +271,12 @@ static int dm_hash_rename(const char *old, const char *new) { char *new_name, *old_name; struct hash_cell *hc; + struct dm_table *table; /* * duplicate new. */ - new_name = kstrdup(new); + new_name = kstrdup(new, GFP_KERNEL); if (!new_name) return -ENOMEM; @@ -316,6 +319,15 @@ static int dm_hash_rename(const char *old, const char *new) /* rename the device node in devfs */ register_with_devfs(hc); + /* + * Wake up any dm event waiters. + */ + table = dm_get_table(hc->md); + if (table) { + dm_table_event(table); + dm_table_put(table); + } + up_write(&_hash_lock); kfree(old_name); return 0; @@ -424,8 +436,8 @@ static void list_version_get_needed(struct target_type *tt, void *needed_param) { size_t *needed = needed_param; + *needed += sizeof(struct dm_target_versions); *needed += strlen(tt->name); - *needed += sizeof(tt->version); *needed += ALIGN_MASK; } @@ -587,17 +599,27 @@ static int dev_create(struct dm_ioctl *param, size_t param_size) /* * Always use UUID for lookups if it's present, otherwise use name or dev. */ -static inline struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) +static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) { + struct mapped_device *md; + void *mdptr = NULL; + if (*param->uuid) return __get_uuid_cell(param->uuid); - else if (*param->name) + + if (*param->name) return __get_name_cell(param->name); - else - return dm_get_mdptr(huge_decode_dev(param->dev)); + + md = dm_get_md(huge_decode_dev(param->dev)); + if (md) { + mdptr = dm_get_mdptr(md); + dm_put(md); + } + + return mdptr; } -static inline struct mapped_device *find_device(struct dm_ioctl *param) +static struct mapped_device *find_device(struct dm_ioctl *param) { struct hash_cell *hc; struct mapped_device *md = NULL; @@ -679,17 +701,69 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size) return dm_hash_rename(param->name, new_name); } +static int dev_set_geometry(struct dm_ioctl *param, size_t param_size) +{ + int r = -EINVAL, x; + struct mapped_device *md; + struct hd_geometry geometry; + unsigned long indata[4]; + char *geostr = (char *) param + param->data_start; + + md = find_device(param); + if (!md) + return -ENXIO; + + if (geostr < (char *) (param + 1) || + invalid_str(geostr, (void *) param + param_size)) { + DMWARN("Invalid geometry supplied."); + goto out; + } + + x = sscanf(geostr, "%lu %lu %lu %lu", indata, + indata + 1, indata + 2, indata + 3); + + if (x != 4) { + DMWARN("Unable to interpret geometry settings."); + goto out; + } + + if (indata[0] > 65535 || indata[1] > 255 || + indata[2] > 255 || indata[3] > ULONG_MAX) { + DMWARN("Geometry exceeds range limits."); + goto out; + } + + geometry.cylinders = indata[0]; + geometry.heads = indata[1]; + geometry.sectors = indata[2]; + geometry.start = indata[3]; + + r = dm_set_geometry(md, &geometry); + if (!r) + r = __dev_status(md, param); + + param->data_size = 0; + +out: + dm_put(md); + return r; +} + static int do_suspend(struct dm_ioctl *param) { int r = 0; + int do_lockfs = 1; struct mapped_device *md; md = find_device(param); if (!md) return -ENXIO; + if (param->flags & DM_SKIP_LOCKFS_FLAG) + do_lockfs = 0; + if (!dm_suspended(md)) - r = dm_suspend(md); + r = dm_suspend(md, do_lockfs); if (!r) r = __dev_status(md, param); @@ -701,6 +775,7 @@ static int do_suspend(struct dm_ioctl *param) static int do_resume(struct dm_ioctl *param) { int r = 0; + int do_lockfs = 1; struct hash_cell *hc; struct mapped_device *md; struct dm_table *new_map; @@ -726,8 +801,10 @@ static int do_resume(struct dm_ioctl *param) /* Do we need to load a new map ? */ if (new_map) { /* Suspend if it isn't already suspended */ + if (param->flags & DM_SKIP_LOCKFS_FLAG) + do_lockfs = 0; if (!dm_suspended(md)) - dm_suspend(md); + dm_suspend(md, do_lockfs); r = dm_swap_table(md, new_map); if (r) { @@ -957,32 +1034,43 @@ static int table_load(struct dm_ioctl *param, size_t param_size) int r; struct hash_cell *hc; struct dm_table *t; + struct mapped_device *md; + + md = find_device(param); + if (!md) + return -ENXIO; - r = dm_table_create(&t, get_mode(param), param->target_count); + r = dm_table_create(&t, get_mode(param), param->target_count, md); if (r) - return r; + goto out; r = populate_table(t, param, param_size); if (r) { dm_table_put(t); - return r; + goto out; } down_write(&_hash_lock); - hc = __find_device_hash_cell(param); - if (!hc) { - DMWARN("device doesn't appear to be in the dev hash table."); + hc = dm_get_mdptr(md); + if (!hc || hc->md != md) { + DMWARN("device has been removed from the dev hash table."); + dm_table_put(t); up_write(&_hash_lock); - return -ENXIO; + r = -ENXIO; + goto out; } if (hc->new_map) dm_table_put(hc->new_map); hc->new_map = t; + up_write(&_hash_lock); + param->flags |= DM_INACTIVE_PRESENT_FLAG; + r = __dev_status(md, param); + +out: + dm_put(md); - r = __dev_status(hc->md, param); - up_write(&_hash_lock); return r; } @@ -1195,7 +1283,8 @@ static ioctl_fn lookup_ioctl(unsigned int cmd) {DM_LIST_VERSIONS_CMD, list_versions}, - {DM_TARGET_MSG_CMD, target_message} + {DM_TARGET_MSG_CMD, target_message}, + {DM_DEV_SET_GEOMETRY_CMD, dev_set_geometry} }; return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; @@ -1340,16 +1429,11 @@ static int ctl_ioctl(struct inode *inode, struct file *file, * Copy the parameters into kernel space. */ r = copy_params(user, ¶m); - 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. - */ + current->flags &= ~PF_MEMALLOC; + + if (r) + return r; r = validate_params(cmd, param); if (r) @@ -1367,7 +1451,6 @@ static int ctl_ioctl(struct inode *inode, struct file *file, out: free_params(param); - current->flags &= ~PF_MEMALLOC; return r; }