ia64/linux-2.6.18-xen.hg

annotate drivers/md/dm-ioctl.c @ 897:329ea0ccb344

balloon: try harder to balloon up under memory pressure.

Currently if the balloon driver is unable to increase the guest's
reservation it assumes the failure was due to reaching its full
allocation, gives up on the ballooning operation and records the limit
it reached as the "hard limit". The driver will not try again until
the target is set again (even to the same value).

However it is possible that ballooning has in fact failed due to
memory pressure in the host and therefore it is desirable to keep
attempting to reach the target in case memory becomes available. The
most likely scenario is that some guests are ballooning down while
others are ballooning up and therefore there is temporary memory
pressure while things stabilise. You would not expect a well behaved
toolstack to ask a domain to balloon to more than its allocation nor
would you expect it to deliberately over-commit memory by setting
balloon targets which exceed the total host memory.

This patch drops the concept of a hard limit and causes the balloon
driver to retry increasing the reservation on a timer in the same
manner as when decreasing the reservation.

Also if we partially succeed in increasing the reservation
(i.e. receive less pages than we asked for) then we may as well keep
those pages rather than returning them to Xen.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 14:01:20 2009 +0100 (2009-06-05)
parents 831230e53067
children
rev   line source
ian@0 1 /*
ian@0 2 * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
ian@0 3 * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
ian@0 4 *
ian@0 5 * This file is released under the GPL.
ian@0 6 */
ian@0 7
ian@0 8 #include "dm.h"
ian@0 9
ian@0 10 #include <linux/module.h>
ian@0 11 #include <linux/vmalloc.h>
ian@0 12 #include <linux/miscdevice.h>
ian@0 13 #include <linux/init.h>
ian@0 14 #include <linux/wait.h>
ian@0 15 #include <linux/slab.h>
ian@0 16 #include <linux/dm-ioctl.h>
ian@0 17 #include <linux/hdreg.h>
ian@0 18
ian@0 19 #include <asm/uaccess.h>
ian@0 20
ian@0 21 #define DM_MSG_PREFIX "ioctl"
ian@0 22 #define DM_DRIVER_EMAIL "dm-devel@redhat.com"
ian@0 23
ian@0 24 /*-----------------------------------------------------------------
ian@0 25 * The ioctl interface needs to be able to look up devices by
ian@0 26 * name or uuid.
ian@0 27 *---------------------------------------------------------------*/
ian@0 28 struct hash_cell {
ian@0 29 struct list_head name_list;
ian@0 30 struct list_head uuid_list;
ian@0 31
ian@0 32 char *name;
ian@0 33 char *uuid;
ian@0 34 struct mapped_device *md;
ian@0 35 struct dm_table *new_map;
ian@0 36 };
ian@0 37
ian@0 38 struct vers_iter {
ian@0 39 size_t param_size;
ian@0 40 struct dm_target_versions *vers, *old_vers;
ian@0 41 char *end;
ian@0 42 uint32_t flags;
ian@0 43 };
ian@0 44
ian@0 45
ian@0 46 #define NUM_BUCKETS 64
ian@0 47 #define MASK_BUCKETS (NUM_BUCKETS - 1)
ian@0 48 static struct list_head _name_buckets[NUM_BUCKETS];
ian@0 49 static struct list_head _uuid_buckets[NUM_BUCKETS];
ian@0 50
ian@0 51 static void dm_hash_remove_all(int keep_open_devices);
ian@0 52
ian@0 53 /*
ian@0 54 * Guards access to both hash tables.
ian@0 55 */
ian@0 56 static DECLARE_RWSEM(_hash_lock);
ian@0 57
ian@0 58 static void init_buckets(struct list_head *buckets)
ian@0 59 {
ian@0 60 unsigned int i;
ian@0 61
ian@0 62 for (i = 0; i < NUM_BUCKETS; i++)
ian@0 63 INIT_LIST_HEAD(buckets + i);
ian@0 64 }
ian@0 65
ian@0 66 static int dm_hash_init(void)
ian@0 67 {
ian@0 68 init_buckets(_name_buckets);
ian@0 69 init_buckets(_uuid_buckets);
ian@0 70 return 0;
ian@0 71 }
ian@0 72
ian@0 73 static void dm_hash_exit(void)
ian@0 74 {
ian@0 75 dm_hash_remove_all(0);
ian@0 76 }
ian@0 77
ian@0 78 /*-----------------------------------------------------------------
ian@0 79 * Hash function:
ian@0 80 * We're not really concerned with the str hash function being
ian@0 81 * fast since it's only used by the ioctl interface.
ian@0 82 *---------------------------------------------------------------*/
ian@0 83 static unsigned int hash_str(const char *str)
ian@0 84 {
ian@0 85 const unsigned int hash_mult = 2654435387U;
ian@0 86 unsigned int h = 0;
ian@0 87
ian@0 88 while (*str)
ian@0 89 h = (h + (unsigned int) *str++) * hash_mult;
ian@0 90
ian@0 91 return h & MASK_BUCKETS;
ian@0 92 }
ian@0 93
ian@0 94 /*-----------------------------------------------------------------
ian@0 95 * Code for looking up a device by name
ian@0 96 *---------------------------------------------------------------*/
ian@0 97 static struct hash_cell *__get_name_cell(const char *str)
ian@0 98 {
ian@0 99 struct hash_cell *hc;
ian@0 100 unsigned int h = hash_str(str);
ian@0 101
ian@0 102 list_for_each_entry (hc, _name_buckets + h, name_list)
ian@0 103 if (!strcmp(hc->name, str)) {
ian@0 104 dm_get(hc->md);
ian@0 105 return hc;
ian@0 106 }
ian@0 107
ian@0 108 return NULL;
ian@0 109 }
ian@0 110
ian@0 111 static struct hash_cell *__get_uuid_cell(const char *str)
ian@0 112 {
ian@0 113 struct hash_cell *hc;
ian@0 114 unsigned int h = hash_str(str);
ian@0 115
ian@0 116 list_for_each_entry (hc, _uuid_buckets + h, uuid_list)
ian@0 117 if (!strcmp(hc->uuid, str)) {
ian@0 118 dm_get(hc->md);
ian@0 119 return hc;
ian@0 120 }
ian@0 121
ian@0 122 return NULL;
ian@0 123 }
ian@0 124
ian@0 125 /*-----------------------------------------------------------------
ian@0 126 * Inserting, removing and renaming a device.
ian@0 127 *---------------------------------------------------------------*/
ian@0 128 static struct hash_cell *alloc_cell(const char *name, const char *uuid,
ian@0 129 struct mapped_device *md)
ian@0 130 {
ian@0 131 struct hash_cell *hc;
ian@0 132
ian@0 133 hc = kmalloc(sizeof(*hc), GFP_KERNEL);
ian@0 134 if (!hc)
ian@0 135 return NULL;
ian@0 136
ian@0 137 hc->name = kstrdup(name, GFP_KERNEL);
ian@0 138 if (!hc->name) {
ian@0 139 kfree(hc);
ian@0 140 return NULL;
ian@0 141 }
ian@0 142
ian@0 143 if (!uuid)
ian@0 144 hc->uuid = NULL;
ian@0 145
ian@0 146 else {
ian@0 147 hc->uuid = kstrdup(uuid, GFP_KERNEL);
ian@0 148 if (!hc->uuid) {
ian@0 149 kfree(hc->name);
ian@0 150 kfree(hc);
ian@0 151 return NULL;
ian@0 152 }
ian@0 153 }
ian@0 154
ian@0 155 INIT_LIST_HEAD(&hc->name_list);
ian@0 156 INIT_LIST_HEAD(&hc->uuid_list);
ian@0 157 hc->md = md;
ian@0 158 hc->new_map = NULL;
ian@0 159 return hc;
ian@0 160 }
ian@0 161
ian@0 162 static void free_cell(struct hash_cell *hc)
ian@0 163 {
ian@0 164 if (hc) {
ian@0 165 kfree(hc->name);
ian@0 166 kfree(hc->uuid);
ian@0 167 kfree(hc);
ian@0 168 }
ian@0 169 }
ian@0 170
ian@0 171 /*
ian@0 172 * The kdev_t and uuid of a device can never change once it is
ian@0 173 * initially inserted.
ian@0 174 */
ian@0 175 static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
ian@0 176 {
ian@0 177 struct hash_cell *cell, *hc;
ian@0 178
ian@0 179 /*
ian@0 180 * Allocate the new cells.
ian@0 181 */
ian@0 182 cell = alloc_cell(name, uuid, md);
ian@0 183 if (!cell)
ian@0 184 return -ENOMEM;
ian@0 185
ian@0 186 /*
ian@0 187 * Insert the cell into both hash tables.
ian@0 188 */
ian@0 189 down_write(&_hash_lock);
ian@0 190 hc = __get_name_cell(name);
ian@0 191 if (hc) {
ian@0 192 dm_put(hc->md);
ian@0 193 goto bad;
ian@0 194 }
ian@0 195
ian@0 196 list_add(&cell->name_list, _name_buckets + hash_str(name));
ian@0 197
ian@0 198 if (uuid) {
ian@0 199 hc = __get_uuid_cell(uuid);
ian@0 200 if (hc) {
ian@0 201 list_del(&cell->name_list);
ian@0 202 dm_put(hc->md);
ian@0 203 goto bad;
ian@0 204 }
ian@0 205 list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
ian@0 206 }
ian@0 207 dm_get(md);
ian@0 208 dm_set_mdptr(md, cell);
ian@0 209 up_write(&_hash_lock);
ian@0 210
ian@0 211 return 0;
ian@0 212
ian@0 213 bad:
ian@0 214 up_write(&_hash_lock);
ian@0 215 free_cell(cell);
ian@0 216 return -EBUSY;
ian@0 217 }
ian@0 218
ian@0 219 static void __hash_remove(struct hash_cell *hc)
ian@0 220 {
ian@0 221 struct dm_table *table;
ian@0 222
ian@0 223 /* remove from the dev hash */
ian@0 224 list_del(&hc->uuid_list);
ian@0 225 list_del(&hc->name_list);
ian@0 226 dm_set_mdptr(hc->md, NULL);
ian@0 227
ian@0 228 table = dm_get_table(hc->md);
ian@0 229 if (table) {
ian@0 230 dm_table_event(table);
ian@0 231 dm_table_put(table);
ian@0 232 }
ian@0 233
ian@0 234 if (hc->new_map)
ian@0 235 dm_table_put(hc->new_map);
ian@0 236 dm_put(hc->md);
ian@0 237 free_cell(hc);
ian@0 238 }
ian@0 239
ian@0 240 static void dm_hash_remove_all(int keep_open_devices)
ian@0 241 {
ian@0 242 int i, dev_skipped, dev_removed;
ian@0 243 struct hash_cell *hc;
ian@0 244 struct list_head *tmp, *n;
ian@0 245
ian@0 246 down_write(&_hash_lock);
ian@0 247
ian@0 248 retry:
ian@0 249 dev_skipped = dev_removed = 0;
ian@0 250 for (i = 0; i < NUM_BUCKETS; i++) {
ian@0 251 list_for_each_safe (tmp, n, _name_buckets + i) {
ian@0 252 hc = list_entry(tmp, struct hash_cell, name_list);
ian@0 253
ian@0 254 if (keep_open_devices &&
ian@0 255 dm_lock_for_deletion(hc->md)) {
ian@0 256 dev_skipped++;
ian@0 257 continue;
ian@0 258 }
ian@0 259 __hash_remove(hc);
ian@0 260 dev_removed = 1;
ian@0 261 }
ian@0 262 }
ian@0 263
ian@0 264 /*
ian@0 265 * Some mapped devices may be using other mapped devices, so if any
ian@0 266 * still exist, repeat until we make no further progress.
ian@0 267 */
ian@0 268 if (dev_skipped) {
ian@0 269 if (dev_removed)
ian@0 270 goto retry;
ian@0 271
ian@0 272 DMWARN("remove_all left %d open device(s)", dev_skipped);
ian@0 273 }
ian@0 274
ian@0 275 up_write(&_hash_lock);
ian@0 276 }
ian@0 277
ian@0 278 static int dm_hash_rename(const char *old, const char *new)
ian@0 279 {
ian@0 280 char *new_name, *old_name;
ian@0 281 struct hash_cell *hc;
ian@0 282 struct dm_table *table;
ian@0 283
ian@0 284 /*
ian@0 285 * duplicate new.
ian@0 286 */
ian@0 287 new_name = kstrdup(new, GFP_KERNEL);
ian@0 288 if (!new_name)
ian@0 289 return -ENOMEM;
ian@0 290
ian@0 291 down_write(&_hash_lock);
ian@0 292
ian@0 293 /*
ian@0 294 * Is new free ?
ian@0 295 */
ian@0 296 hc = __get_name_cell(new);
ian@0 297 if (hc) {
ian@0 298 DMWARN("asked to rename to an already existing name %s -> %s",
ian@0 299 old, new);
ian@0 300 dm_put(hc->md);
ian@0 301 up_write(&_hash_lock);
ian@0 302 kfree(new_name);
ian@0 303 return -EBUSY;
ian@0 304 }
ian@0 305
ian@0 306 /*
ian@0 307 * Is there such a device as 'old' ?
ian@0 308 */
ian@0 309 hc = __get_name_cell(old);
ian@0 310 if (!hc) {
ian@0 311 DMWARN("asked to rename a non existent device %s -> %s",
ian@0 312 old, new);
ian@0 313 up_write(&_hash_lock);
ian@0 314 kfree(new_name);
ian@0 315 return -ENXIO;
ian@0 316 }
ian@0 317
ian@0 318 /*
ian@0 319 * rename and move the name cell.
ian@0 320 */
ian@0 321 list_del(&hc->name_list);
ian@0 322 old_name = hc->name;
ian@0 323 hc->name = new_name;
ian@0 324 list_add(&hc->name_list, _name_buckets + hash_str(new_name));
ian@0 325
ian@0 326 /*
ian@0 327 * Wake up any dm event waiters.
ian@0 328 */
ian@0 329 table = dm_get_table(hc->md);
ian@0 330 if (table) {
ian@0 331 dm_table_event(table);
ian@0 332 dm_table_put(table);
ian@0 333 }
ian@0 334
ian@0 335 dm_put(hc->md);
ian@0 336 up_write(&_hash_lock);
ian@0 337 kfree(old_name);
ian@0 338 return 0;
ian@0 339 }
ian@0 340
ian@0 341 /*-----------------------------------------------------------------
ian@0 342 * Implementation of the ioctl commands
ian@0 343 *---------------------------------------------------------------*/
ian@0 344 /*
ian@0 345 * All the ioctl commands get dispatched to functions with this
ian@0 346 * prototype.
ian@0 347 */
ian@0 348 typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
ian@0 349
ian@0 350 static int remove_all(struct dm_ioctl *param, size_t param_size)
ian@0 351 {
ian@0 352 dm_hash_remove_all(1);
ian@0 353 param->data_size = 0;
ian@0 354 return 0;
ian@0 355 }
ian@0 356
ian@0 357 /*
ian@0 358 * Round up the ptr to an 8-byte boundary.
ian@0 359 */
ian@0 360 #define ALIGN_MASK 7
ian@0 361 static inline void *align_ptr(void *ptr)
ian@0 362 {
ian@0 363 return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK);
ian@0 364 }
ian@0 365
ian@0 366 /*
ian@0 367 * Retrieves the data payload buffer from an already allocated
ian@0 368 * struct dm_ioctl.
ian@0 369 */
ian@0 370 static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
ian@0 371 size_t *len)
ian@0 372 {
ian@0 373 param->data_start = align_ptr(param + 1) - (void *) param;
ian@0 374
ian@0 375 if (param->data_start < param_size)
ian@0 376 *len = param_size - param->data_start;
ian@0 377 else
ian@0 378 *len = 0;
ian@0 379
ian@0 380 return ((void *) param) + param->data_start;
ian@0 381 }
ian@0 382
ian@0 383 static int list_devices(struct dm_ioctl *param, size_t param_size)
ian@0 384 {
ian@0 385 unsigned int i;
ian@0 386 struct hash_cell *hc;
ian@0 387 size_t len, needed = 0;
ian@0 388 struct gendisk *disk;
ian@0 389 struct dm_name_list *nl, *old_nl = NULL;
ian@0 390
ian@0 391 down_write(&_hash_lock);
ian@0 392
ian@0 393 /*
ian@0 394 * Loop through all the devices working out how much
ian@0 395 * space we need.
ian@0 396 */
ian@0 397 for (i = 0; i < NUM_BUCKETS; i++) {
ian@0 398 list_for_each_entry (hc, _name_buckets + i, name_list) {
ian@0 399 needed += sizeof(struct dm_name_list);
ian@0 400 needed += strlen(hc->name) + 1;
ian@0 401 needed += ALIGN_MASK;
ian@0 402 }
ian@0 403 }
ian@0 404
ian@0 405 /*
ian@0 406 * Grab our output buffer.
ian@0 407 */
ian@0 408 nl = get_result_buffer(param, param_size, &len);
ian@0 409 if (len < needed) {
ian@0 410 param->flags |= DM_BUFFER_FULL_FLAG;
ian@0 411 goto out;
ian@0 412 }
ian@0 413 param->data_size = param->data_start + needed;
ian@0 414
ian@0 415 nl->dev = 0; /* Flags no data */
ian@0 416
ian@0 417 /*
ian@0 418 * Now loop through filling out the names.
ian@0 419 */
ian@0 420 for (i = 0; i < NUM_BUCKETS; i++) {
ian@0 421 list_for_each_entry (hc, _name_buckets + i, name_list) {
ian@0 422 if (old_nl)
ian@0 423 old_nl->next = (uint32_t) ((void *) nl -
ian@0 424 (void *) old_nl);
ian@0 425 disk = dm_disk(hc->md);
ian@0 426 nl->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor));
ian@0 427 nl->next = 0;
ian@0 428 strcpy(nl->name, hc->name);
ian@0 429
ian@0 430 old_nl = nl;
ian@0 431 nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
ian@0 432 }
ian@0 433 }
ian@0 434
ian@0 435 out:
ian@0 436 up_write(&_hash_lock);
ian@0 437 return 0;
ian@0 438 }
ian@0 439
ian@0 440 static void list_version_get_needed(struct target_type *tt, void *needed_param)
ian@0 441 {
ian@0 442 size_t *needed = needed_param;
ian@0 443
ian@0 444 *needed += sizeof(struct dm_target_versions);
ian@0 445 *needed += strlen(tt->name);
ian@0 446 *needed += ALIGN_MASK;
ian@0 447 }
ian@0 448
ian@0 449 static void list_version_get_info(struct target_type *tt, void *param)
ian@0 450 {
ian@0 451 struct vers_iter *info = param;
ian@0 452
ian@0 453 /* Check space - it might have changed since the first iteration */
ian@0 454 if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 >
ian@0 455 info->end) {
ian@0 456
ian@0 457 info->flags = DM_BUFFER_FULL_FLAG;
ian@0 458 return;
ian@0 459 }
ian@0 460
ian@0 461 if (info->old_vers)
ian@0 462 info->old_vers->next = (uint32_t) ((void *)info->vers -
ian@0 463 (void *)info->old_vers);
ian@0 464 info->vers->version[0] = tt->version[0];
ian@0 465 info->vers->version[1] = tt->version[1];
ian@0 466 info->vers->version[2] = tt->version[2];
ian@0 467 info->vers->next = 0;
ian@0 468 strcpy(info->vers->name, tt->name);
ian@0 469
ian@0 470 info->old_vers = info->vers;
ian@0 471 info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1);
ian@0 472 }
ian@0 473
ian@0 474 static int list_versions(struct dm_ioctl *param, size_t param_size)
ian@0 475 {
ian@0 476 size_t len, needed = 0;
ian@0 477 struct dm_target_versions *vers;
ian@0 478 struct vers_iter iter_info;
ian@0 479
ian@0 480 /*
ian@0 481 * Loop through all the devices working out how much
ian@0 482 * space we need.
ian@0 483 */
ian@0 484 dm_target_iterate(list_version_get_needed, &needed);
ian@0 485
ian@0 486 /*
ian@0 487 * Grab our output buffer.
ian@0 488 */
ian@0 489 vers = get_result_buffer(param, param_size, &len);
ian@0 490 if (len < needed) {
ian@0 491 param->flags |= DM_BUFFER_FULL_FLAG;
ian@0 492 goto out;
ian@0 493 }
ian@0 494 param->data_size = param->data_start + needed;
ian@0 495
ian@0 496 iter_info.param_size = param_size;
ian@0 497 iter_info.old_vers = NULL;
ian@0 498 iter_info.vers = vers;
ian@0 499 iter_info.flags = 0;
ian@0 500 iter_info.end = (char *)vers+len;
ian@0 501
ian@0 502 /*
ian@0 503 * Now loop through filling out the names & versions.
ian@0 504 */
ian@0 505 dm_target_iterate(list_version_get_info, &iter_info);
ian@0 506 param->flags |= iter_info.flags;
ian@0 507
ian@0 508 out:
ian@0 509 return 0;
ian@0 510 }
ian@0 511
ian@0 512
ian@0 513
ian@0 514 static int check_name(const char *name)
ian@0 515 {
ian@0 516 if (strchr(name, '/')) {
ian@0 517 DMWARN("invalid device name");
ian@0 518 return -EINVAL;
ian@0 519 }
ian@0 520
ian@0 521 return 0;
ian@0 522 }
ian@0 523
ian@0 524 /*
ian@0 525 * Fills in a dm_ioctl structure, ready for sending back to
ian@0 526 * userland.
ian@0 527 */
ian@0 528 static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
ian@0 529 {
ian@0 530 struct gendisk *disk = dm_disk(md);
ian@0 531 struct dm_table *table;
ian@0 532
ian@0 533 param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
ian@0 534 DM_ACTIVE_PRESENT_FLAG);
ian@0 535
ian@0 536 if (dm_suspended(md))
ian@0 537 param->flags |= DM_SUSPEND_FLAG;
ian@0 538
ian@0 539 param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor));
ian@0 540
ian@0 541 /*
ian@0 542 * Yes, this will be out of date by the time it gets back
ian@0 543 * to userland, but it is still very useful for
ian@0 544 * debugging.
ian@0 545 */
ian@0 546 param->open_count = dm_open_count(md);
ian@0 547
ian@0 548 if (disk->policy)
ian@0 549 param->flags |= DM_READONLY_FLAG;
ian@0 550
ian@0 551 param->event_nr = dm_get_event_nr(md);
ian@0 552
ian@0 553 table = dm_get_table(md);
ian@0 554 if (table) {
ian@0 555 param->flags |= DM_ACTIVE_PRESENT_FLAG;
ian@0 556 param->target_count = dm_table_get_num_targets(table);
ian@0 557 dm_table_put(table);
ian@0 558 } else
ian@0 559 param->target_count = 0;
ian@0 560
ian@0 561 return 0;
ian@0 562 }
ian@0 563
ian@0 564 static int dev_create(struct dm_ioctl *param, size_t param_size)
ian@0 565 {
ian@0 566 int r, m = DM_ANY_MINOR;
ian@0 567 struct mapped_device *md;
ian@0 568
ian@0 569 r = check_name(param->name);
ian@0 570 if (r)
ian@0 571 return r;
ian@0 572
ian@0 573 if (param->flags & DM_PERSISTENT_DEV_FLAG)
ian@0 574 m = MINOR(huge_decode_dev(param->dev));
ian@0 575
ian@0 576 r = dm_create(m, &md);
ian@0 577 if (r)
ian@0 578 return r;
ian@0 579
ian@0 580 r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
ian@0 581 if (r) {
ian@0 582 dm_put(md);
ian@0 583 return r;
ian@0 584 }
ian@0 585
ian@0 586 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
ian@0 587
ian@0 588 r = __dev_status(md, param);
ian@0 589 dm_put(md);
ian@0 590
ian@0 591 return r;
ian@0 592 }
ian@0 593
ian@0 594 /*
ian@0 595 * Always use UUID for lookups if it's present, otherwise use name or dev.
ian@0 596 */
ian@0 597 static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
ian@0 598 {
ian@0 599 struct mapped_device *md;
ian@0 600 void *mdptr = NULL;
ian@0 601
ian@0 602 if (*param->uuid)
ian@0 603 return __get_uuid_cell(param->uuid);
ian@0 604
ian@0 605 if (*param->name)
ian@0 606 return __get_name_cell(param->name);
ian@0 607
ian@0 608 md = dm_get_md(huge_decode_dev(param->dev));
ian@0 609 if (md)
ian@0 610 mdptr = dm_get_mdptr(md);
ian@0 611
ian@0 612 return mdptr;
ian@0 613 }
ian@0 614
ian@0 615 static struct mapped_device *find_device(struct dm_ioctl *param)
ian@0 616 {
ian@0 617 struct hash_cell *hc;
ian@0 618 struct mapped_device *md = NULL;
ian@0 619
ian@0 620 down_read(&_hash_lock);
ian@0 621 hc = __find_device_hash_cell(param);
ian@0 622 if (hc) {
ian@0 623 md = hc->md;
ian@0 624
ian@0 625 /*
ian@0 626 * Sneakily write in both the name and the uuid
ian@0 627 * while we have the cell.
ian@0 628 */
ian@0 629 strncpy(param->name, hc->name, sizeof(param->name));
ian@0 630 if (hc->uuid)
ian@0 631 strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1);
ian@0 632 else
ian@0 633 param->uuid[0] = '\0';
ian@0 634
ian@0 635 if (hc->new_map)
ian@0 636 param->flags |= DM_INACTIVE_PRESENT_FLAG;
ian@0 637 else
ian@0 638 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
ian@0 639 }
ian@0 640 up_read(&_hash_lock);
ian@0 641
ian@0 642 return md;
ian@0 643 }
ian@0 644
ian@0 645 static int dev_remove(struct dm_ioctl *param, size_t param_size)
ian@0 646 {
ian@0 647 struct hash_cell *hc;
ian@0 648 struct mapped_device *md;
ian@0 649 int r;
ian@0 650
ian@0 651 down_write(&_hash_lock);
ian@0 652 hc = __find_device_hash_cell(param);
ian@0 653
ian@0 654 if (!hc) {
ian@0 655 DMWARN("device doesn't appear to be in the dev hash table.");
ian@0 656 up_write(&_hash_lock);
ian@0 657 return -ENXIO;
ian@0 658 }
ian@0 659
ian@0 660 md = hc->md;
ian@0 661
ian@0 662 /*
ian@0 663 * Ensure the device is not open and nothing further can open it.
ian@0 664 */
ian@0 665 r = dm_lock_for_deletion(md);
ian@0 666 if (r) {
ian@0 667 DMWARN("unable to remove open device %s", hc->name);
ian@0 668 up_write(&_hash_lock);
ian@0 669 dm_put(md);
ian@0 670 return r;
ian@0 671 }
ian@0 672
ian@0 673 __hash_remove(hc);
ian@0 674 up_write(&_hash_lock);
ian@0 675 dm_put(md);
ian@0 676 param->data_size = 0;
ian@0 677 return 0;
ian@0 678 }
ian@0 679
ian@0 680 /*
ian@0 681 * Check a string doesn't overrun the chunk of
ian@0 682 * memory we copied from userland.
ian@0 683 */
ian@0 684 static int invalid_str(char *str, void *end)
ian@0 685 {
ian@0 686 while ((void *) str < end)
ian@0 687 if (!*str++)
ian@0 688 return 0;
ian@0 689
ian@0 690 return -EINVAL;
ian@0 691 }
ian@0 692
ian@0 693 static int dev_rename(struct dm_ioctl *param, size_t param_size)
ian@0 694 {
ian@0 695 int r;
ian@0 696 char *new_name = (char *) param + param->data_start;
ian@0 697
ian@0 698 if (new_name < (char *) (param + 1) ||
ian@0 699 invalid_str(new_name, (void *) param + param_size)) {
ian@0 700 DMWARN("Invalid new logical volume name supplied.");
ian@0 701 return -EINVAL;
ian@0 702 }
ian@0 703
ian@0 704 r = check_name(new_name);
ian@0 705 if (r)
ian@0 706 return r;
ian@0 707
ian@0 708 param->data_size = 0;
ian@0 709 return dm_hash_rename(param->name, new_name);
ian@0 710 }
ian@0 711
ian@0 712 static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
ian@0 713 {
ian@0 714 int r = -EINVAL, x;
ian@0 715 struct mapped_device *md;
ian@0 716 struct hd_geometry geometry;
ian@0 717 unsigned long indata[4];
ian@0 718 char *geostr = (char *) param + param->data_start;
ian@0 719
ian@0 720 md = find_device(param);
ian@0 721 if (!md)
ian@0 722 return -ENXIO;
ian@0 723
ian@0 724 if (geostr < (char *) (param + 1) ||
ian@0 725 invalid_str(geostr, (void *) param + param_size)) {
ian@0 726 DMWARN("Invalid geometry supplied.");
ian@0 727 goto out;
ian@0 728 }
ian@0 729
ian@0 730 x = sscanf(geostr, "%lu %lu %lu %lu", indata,
ian@0 731 indata + 1, indata + 2, indata + 3);
ian@0 732
ian@0 733 if (x != 4) {
ian@0 734 DMWARN("Unable to interpret geometry settings.");
ian@0 735 goto out;
ian@0 736 }
ian@0 737
ian@0 738 if (indata[0] > 65535 || indata[1] > 255 ||
ian@0 739 indata[2] > 255 || indata[3] > ULONG_MAX) {
ian@0 740 DMWARN("Geometry exceeds range limits.");
ian@0 741 goto out;
ian@0 742 }
ian@0 743
ian@0 744 geometry.cylinders = indata[0];
ian@0 745 geometry.heads = indata[1];
ian@0 746 geometry.sectors = indata[2];
ian@0 747 geometry.start = indata[3];
ian@0 748
ian@0 749 r = dm_set_geometry(md, &geometry);
ian@0 750 if (!r)
ian@0 751 r = __dev_status(md, param);
ian@0 752
ian@0 753 param->data_size = 0;
ian@0 754
ian@0 755 out:
ian@0 756 dm_put(md);
ian@0 757 return r;
ian@0 758 }
ian@0 759
ian@0 760 static int do_suspend(struct dm_ioctl *param)
ian@0 761 {
ian@0 762 int r = 0;
ian@0 763 int do_lockfs = 1;
ian@0 764 struct mapped_device *md;
ian@0 765
ian@0 766 md = find_device(param);
ian@0 767 if (!md)
ian@0 768 return -ENXIO;
ian@0 769
ian@0 770 if (param->flags & DM_SKIP_LOCKFS_FLAG)
ian@0 771 do_lockfs = 0;
ian@0 772
ian@0 773 if (!dm_suspended(md))
ian@0 774 r = dm_suspend(md, do_lockfs);
ian@0 775
ian@0 776 if (!r)
ian@0 777 r = __dev_status(md, param);
ian@0 778
ian@0 779 dm_put(md);
ian@0 780 return r;
ian@0 781 }
ian@0 782
ian@0 783 static int do_resume(struct dm_ioctl *param)
ian@0 784 {
ian@0 785 int r = 0;
ian@0 786 int do_lockfs = 1;
ian@0 787 struct hash_cell *hc;
ian@0 788 struct mapped_device *md;
ian@0 789 struct dm_table *new_map;
ian@0 790
ian@0 791 down_write(&_hash_lock);
ian@0 792
ian@0 793 hc = __find_device_hash_cell(param);
ian@0 794 if (!hc) {
ian@0 795 DMWARN("device doesn't appear to be in the dev hash table.");
ian@0 796 up_write(&_hash_lock);
ian@0 797 return -ENXIO;
ian@0 798 }
ian@0 799
ian@0 800 md = hc->md;
ian@0 801
ian@0 802 new_map = hc->new_map;
ian@0 803 hc->new_map = NULL;
ian@0 804 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
ian@0 805
ian@0 806 up_write(&_hash_lock);
ian@0 807
ian@0 808 /* Do we need to load a new map ? */
ian@0 809 if (new_map) {
ian@0 810 /* Suspend if it isn't already suspended */
ian@0 811 if (param->flags & DM_SKIP_LOCKFS_FLAG)
ian@0 812 do_lockfs = 0;
ian@0 813 if (!dm_suspended(md))
ian@0 814 dm_suspend(md, do_lockfs);
ian@0 815
ian@0 816 r = dm_swap_table(md, new_map);
ian@0 817 if (r) {
ian@0 818 dm_put(md);
ian@0 819 dm_table_put(new_map);
ian@0 820 return r;
ian@0 821 }
ian@0 822
ian@0 823 if (dm_table_get_mode(new_map) & FMODE_WRITE)
ian@0 824 set_disk_ro(dm_disk(md), 0);
ian@0 825 else
ian@0 826 set_disk_ro(dm_disk(md), 1);
ian@0 827
ian@0 828 dm_table_put(new_map);
ian@0 829 }
ian@0 830
ian@0 831 if (dm_suspended(md))
ian@0 832 r = dm_resume(md);
ian@0 833
ian@0 834 if (!r)
ian@0 835 r = __dev_status(md, param);
ian@0 836
ian@0 837 dm_put(md);
ian@0 838 return r;
ian@0 839 }
ian@0 840
ian@0 841 /*
ian@0 842 * Set or unset the suspension state of a device.
ian@0 843 * If the device already is in the requested state we just return its status.
ian@0 844 */
ian@0 845 static int dev_suspend(struct dm_ioctl *param, size_t param_size)
ian@0 846 {
ian@0 847 if (param->flags & DM_SUSPEND_FLAG)
ian@0 848 return do_suspend(param);
ian@0 849
ian@0 850 return do_resume(param);
ian@0 851 }
ian@0 852
ian@0 853 /*
ian@0 854 * Copies device info back to user space, used by
ian@0 855 * the create and info ioctls.
ian@0 856 */
ian@0 857 static int dev_status(struct dm_ioctl *param, size_t param_size)
ian@0 858 {
ian@0 859 int r;
ian@0 860 struct mapped_device *md;
ian@0 861
ian@0 862 md = find_device(param);
ian@0 863 if (!md)
ian@0 864 return -ENXIO;
ian@0 865
ian@0 866 r = __dev_status(md, param);
ian@0 867 dm_put(md);
ian@0 868 return r;
ian@0 869 }
ian@0 870
ian@0 871 /*
ian@0 872 * Build up the status struct for each target
ian@0 873 */
ian@0 874 static void retrieve_status(struct dm_table *table,
ian@0 875 struct dm_ioctl *param, size_t param_size)
ian@0 876 {
ian@0 877 unsigned int i, num_targets;
ian@0 878 struct dm_target_spec *spec;
ian@0 879 char *outbuf, *outptr;
ian@0 880 status_type_t type;
ian@0 881 size_t remaining, len, used = 0;
ian@0 882
ian@0 883 outptr = outbuf = get_result_buffer(param, param_size, &len);
ian@0 884
ian@0 885 if (param->flags & DM_STATUS_TABLE_FLAG)
ian@0 886 type = STATUSTYPE_TABLE;
ian@0 887 else
ian@0 888 type = STATUSTYPE_INFO;
ian@0 889
ian@0 890 /* Get all the target info */
ian@0 891 num_targets = dm_table_get_num_targets(table);
ian@0 892 for (i = 0; i < num_targets; i++) {
ian@0 893 struct dm_target *ti = dm_table_get_target(table, i);
ian@0 894
ian@0 895 remaining = len - (outptr - outbuf);
ian@0 896 if (remaining <= sizeof(struct dm_target_spec)) {
ian@0 897 param->flags |= DM_BUFFER_FULL_FLAG;
ian@0 898 break;
ian@0 899 }
ian@0 900
ian@0 901 spec = (struct dm_target_spec *) outptr;
ian@0 902
ian@0 903 spec->status = 0;
ian@0 904 spec->sector_start = ti->begin;
ian@0 905 spec->length = ti->len;
ian@0 906 strncpy(spec->target_type, ti->type->name,
ian@0 907 sizeof(spec->target_type));
ian@0 908
ian@0 909 outptr += sizeof(struct dm_target_spec);
ian@0 910 remaining = len - (outptr - outbuf);
ian@0 911 if (remaining <= 0) {
ian@0 912 param->flags |= DM_BUFFER_FULL_FLAG;
ian@0 913 break;
ian@0 914 }
ian@0 915
ian@0 916 /* Get the status/table string from the target driver */
ian@0 917 if (ti->type->status) {
ian@0 918 if (ti->type->status(ti, type, outptr, remaining)) {
ian@0 919 param->flags |= DM_BUFFER_FULL_FLAG;
ian@0 920 break;
ian@0 921 }
ian@0 922 } else
ian@0 923 outptr[0] = '\0';
ian@0 924
ian@0 925 outptr += strlen(outptr) + 1;
ian@0 926 used = param->data_start + (outptr - outbuf);
ian@0 927
ian@0 928 outptr = align_ptr(outptr);
ian@0 929 spec->next = outptr - outbuf;
ian@0 930 }
ian@0 931
ian@0 932 if (used)
ian@0 933 param->data_size = used;
ian@0 934
ian@0 935 param->target_count = num_targets;
ian@0 936 }
ian@0 937
ian@0 938 /*
ian@0 939 * Wait for a device to report an event
ian@0 940 */
ian@0 941 static int dev_wait(struct dm_ioctl *param, size_t param_size)
ian@0 942 {
ian@0 943 int r;
ian@0 944 struct mapped_device *md;
ian@0 945 struct dm_table *table;
ian@0 946
ian@0 947 md = find_device(param);
ian@0 948 if (!md)
ian@0 949 return -ENXIO;
ian@0 950
ian@0 951 /*
ian@0 952 * Wait for a notification event
ian@0 953 */
ian@0 954 if (dm_wait_event(md, param->event_nr)) {
ian@0 955 r = -ERESTARTSYS;
ian@0 956 goto out;
ian@0 957 }
ian@0 958
ian@0 959 /*
ian@0 960 * The userland program is going to want to know what
ian@0 961 * changed to trigger the event, so we may as well tell
ian@0 962 * him and save an ioctl.
ian@0 963 */
ian@0 964 r = __dev_status(md, param);
ian@0 965 if (r)
ian@0 966 goto out;
ian@0 967
ian@0 968 table = dm_get_table(md);
ian@0 969 if (table) {
ian@0 970 retrieve_status(table, param, param_size);
ian@0 971 dm_table_put(table);
ian@0 972 }
ian@0 973
ian@0 974 out:
ian@0 975 dm_put(md);
ian@0 976 return r;
ian@0 977 }
ian@0 978
ian@0 979 static inline int get_mode(struct dm_ioctl *param)
ian@0 980 {
ian@0 981 int mode = FMODE_READ | FMODE_WRITE;
ian@0 982
ian@0 983 if (param->flags & DM_READONLY_FLAG)
ian@0 984 mode = FMODE_READ;
ian@0 985
ian@0 986 return mode;
ian@0 987 }
ian@0 988
ian@0 989 static int next_target(struct dm_target_spec *last, uint32_t next, void *end,
ian@0 990 struct dm_target_spec **spec, char **target_params)
ian@0 991 {
ian@0 992 *spec = (struct dm_target_spec *) ((unsigned char *) last + next);
ian@0 993 *target_params = (char *) (*spec + 1);
ian@0 994
ian@0 995 if (*spec < (last + 1))
ian@0 996 return -EINVAL;
ian@0 997
ian@0 998 return invalid_str(*target_params, end);
ian@0 999 }
ian@0 1000
ian@0 1001 static int populate_table(struct dm_table *table,
ian@0 1002 struct dm_ioctl *param, size_t param_size)
ian@0 1003 {
ian@0 1004 int r;
ian@0 1005 unsigned int i = 0;
ian@0 1006 struct dm_target_spec *spec = (struct dm_target_spec *) param;
ian@0 1007 uint32_t next = param->data_start;
ian@0 1008 void *end = (void *) param + param_size;
ian@0 1009 char *target_params;
ian@0 1010
ian@0 1011 if (!param->target_count) {
ian@0 1012 DMWARN("populate_table: no targets specified");
ian@0 1013 return -EINVAL;
ian@0 1014 }
ian@0 1015
ian@0 1016 for (i = 0; i < param->target_count; i++) {
ian@0 1017
ian@0 1018 r = next_target(spec, next, end, &spec, &target_params);
ian@0 1019 if (r) {
ian@0 1020 DMWARN("unable to find target");
ian@0 1021 return r;
ian@0 1022 }
ian@0 1023
ian@0 1024 r = dm_table_add_target(table, spec->target_type,
ian@0 1025 (sector_t) spec->sector_start,
ian@0 1026 (sector_t) spec->length,
ian@0 1027 target_params);
ian@0 1028 if (r) {
ian@0 1029 DMWARN("error adding target to table");
ian@0 1030 return r;
ian@0 1031 }
ian@0 1032
ian@0 1033 next = spec->next;
ian@0 1034 }
ian@0 1035
ian@0 1036 return dm_table_complete(table);
ian@0 1037 }
ian@0 1038
ian@0 1039 static int table_load(struct dm_ioctl *param, size_t param_size)
ian@0 1040 {
ian@0 1041 int r;
ian@0 1042 struct hash_cell *hc;
ian@0 1043 struct dm_table *t;
ian@0 1044 struct mapped_device *md;
ian@0 1045
ian@0 1046 md = find_device(param);
ian@0 1047 if (!md)
ian@0 1048 return -ENXIO;
ian@0 1049
ian@0 1050 r = dm_table_create(&t, get_mode(param), param->target_count, md);
ian@0 1051 if (r)
ian@0 1052 goto out;
ian@0 1053
ian@0 1054 r = populate_table(t, param, param_size);
ian@0 1055 if (r) {
ian@0 1056 dm_table_put(t);
ian@0 1057 goto out;
ian@0 1058 }
ian@0 1059
ian@0 1060 down_write(&_hash_lock);
ian@0 1061 hc = dm_get_mdptr(md);
ian@0 1062 if (!hc || hc->md != md) {
ian@0 1063 DMWARN("device has been removed from the dev hash table.");
ian@0 1064 dm_table_put(t);
ian@0 1065 up_write(&_hash_lock);
ian@0 1066 r = -ENXIO;
ian@0 1067 goto out;
ian@0 1068 }
ian@0 1069
ian@0 1070 if (hc->new_map)
ian@0 1071 dm_table_put(hc->new_map);
ian@0 1072 hc->new_map = t;
ian@0 1073 up_write(&_hash_lock);
ian@0 1074
ian@0 1075 param->flags |= DM_INACTIVE_PRESENT_FLAG;
ian@0 1076 r = __dev_status(md, param);
ian@0 1077
ian@0 1078 out:
ian@0 1079 dm_put(md);
ian@0 1080
ian@0 1081 return r;
ian@0 1082 }
ian@0 1083
ian@0 1084 static int table_clear(struct dm_ioctl *param, size_t param_size)
ian@0 1085 {
ian@0 1086 int r;
ian@0 1087 struct hash_cell *hc;
ian@0 1088 struct mapped_device *md;
ian@0 1089
ian@0 1090 down_write(&_hash_lock);
ian@0 1091
ian@0 1092 hc = __find_device_hash_cell(param);
ian@0 1093 if (!hc) {
ian@0 1094 DMWARN("device doesn't appear to be in the dev hash table.");
ian@0 1095 up_write(&_hash_lock);
ian@0 1096 return -ENXIO;
ian@0 1097 }
ian@0 1098
ian@0 1099 if (hc->new_map) {
ian@0 1100 dm_table_put(hc->new_map);
ian@0 1101 hc->new_map = NULL;
ian@0 1102 }
ian@0 1103
ian@0 1104 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
ian@0 1105
ian@0 1106 r = __dev_status(hc->md, param);
ian@0 1107 md = hc->md;
ian@0 1108 up_write(&_hash_lock);
ian@0 1109 dm_put(md);
ian@0 1110 return r;
ian@0 1111 }
ian@0 1112
ian@0 1113 /*
ian@0 1114 * Retrieves a list of devices used by a particular dm device.
ian@0 1115 */
ian@0 1116 static void retrieve_deps(struct dm_table *table,
ian@0 1117 struct dm_ioctl *param, size_t param_size)
ian@0 1118 {
ian@0 1119 unsigned int count = 0;
ian@0 1120 struct list_head *tmp;
ian@0 1121 size_t len, needed;
ian@0 1122 struct dm_dev *dd;
ian@0 1123 struct dm_target_deps *deps;
ian@0 1124
ian@0 1125 deps = get_result_buffer(param, param_size, &len);
ian@0 1126
ian@0 1127 /*
ian@0 1128 * Count the devices.
ian@0 1129 */
ian@0 1130 list_for_each (tmp, dm_table_get_devices(table))
ian@0 1131 count++;
ian@0 1132
ian@0 1133 /*
ian@0 1134 * Check we have enough space.
ian@0 1135 */
ian@0 1136 needed = sizeof(*deps) + (sizeof(*deps->dev) * count);
ian@0 1137 if (len < needed) {
ian@0 1138 param->flags |= DM_BUFFER_FULL_FLAG;
ian@0 1139 return;
ian@0 1140 }
ian@0 1141
ian@0 1142 /*
ian@0 1143 * Fill in the devices.
ian@0 1144 */
ian@0 1145 deps->count = count;
ian@0 1146 count = 0;
ian@0 1147 list_for_each_entry (dd, dm_table_get_devices(table), list)
ian@0 1148 deps->dev[count++] = huge_encode_dev(dd->bdev->bd_dev);
ian@0 1149
ian@0 1150 param->data_size = param->data_start + needed;
ian@0 1151 }
ian@0 1152
ian@0 1153 static int table_deps(struct dm_ioctl *param, size_t param_size)
ian@0 1154 {
ian@0 1155 int r = 0;
ian@0 1156 struct mapped_device *md;
ian@0 1157 struct dm_table *table;
ian@0 1158
ian@0 1159 md = find_device(param);
ian@0 1160 if (!md)
ian@0 1161 return -ENXIO;
ian@0 1162
ian@0 1163 r = __dev_status(md, param);
ian@0 1164 if (r)
ian@0 1165 goto out;
ian@0 1166
ian@0 1167 table = dm_get_table(md);
ian@0 1168 if (table) {
ian@0 1169 retrieve_deps(table, param, param_size);
ian@0 1170 dm_table_put(table);
ian@0 1171 }
ian@0 1172
ian@0 1173 out:
ian@0 1174 dm_put(md);
ian@0 1175 return r;
ian@0 1176 }
ian@0 1177
ian@0 1178 /*
ian@0 1179 * Return the status of a device as a text string for each
ian@0 1180 * target.
ian@0 1181 */
ian@0 1182 static int table_status(struct dm_ioctl *param, size_t param_size)
ian@0 1183 {
ian@0 1184 int r;
ian@0 1185 struct mapped_device *md;
ian@0 1186 struct dm_table *table;
ian@0 1187
ian@0 1188 md = find_device(param);
ian@0 1189 if (!md)
ian@0 1190 return -ENXIO;
ian@0 1191
ian@0 1192 r = __dev_status(md, param);
ian@0 1193 if (r)
ian@0 1194 goto out;
ian@0 1195
ian@0 1196 table = dm_get_table(md);
ian@0 1197 if (table) {
ian@0 1198 retrieve_status(table, param, param_size);
ian@0 1199 dm_table_put(table);
ian@0 1200 }
ian@0 1201
ian@0 1202 out:
ian@0 1203 dm_put(md);
ian@0 1204 return r;
ian@0 1205 }
ian@0 1206
ian@0 1207 /*
ian@0 1208 * Pass a message to the target that's at the supplied device offset.
ian@0 1209 */
ian@0 1210 static int target_message(struct dm_ioctl *param, size_t param_size)
ian@0 1211 {
ian@0 1212 int r, argc;
ian@0 1213 char **argv;
ian@0 1214 struct mapped_device *md;
ian@0 1215 struct dm_table *table;
ian@0 1216 struct dm_target *ti;
ian@0 1217 struct dm_target_msg *tmsg = (void *) param + param->data_start;
ian@0 1218
ian@0 1219 md = find_device(param);
ian@0 1220 if (!md)
ian@0 1221 return -ENXIO;
ian@0 1222
ian@0 1223 r = __dev_status(md, param);
ian@0 1224 if (r)
ian@0 1225 goto out;
ian@0 1226
ian@0 1227 if (tmsg < (struct dm_target_msg *) (param + 1) ||
ian@0 1228 invalid_str(tmsg->message, (void *) param + param_size)) {
ian@0 1229 DMWARN("Invalid target message parameters.");
ian@0 1230 r = -EINVAL;
ian@0 1231 goto out;
ian@0 1232 }
ian@0 1233
ian@0 1234 r = dm_split_args(&argc, &argv, tmsg->message);
ian@0 1235 if (r) {
ian@0 1236 DMWARN("Failed to split target message parameters");
ian@0 1237 goto out;
ian@0 1238 }
ian@0 1239
ian@0 1240 table = dm_get_table(md);
ian@0 1241 if (!table)
ian@0 1242 goto out_argv;
ian@0 1243
ian@0 1244 if (tmsg->sector >= dm_table_get_size(table)) {
ian@0 1245 DMWARN("Target message sector outside device.");
ian@0 1246 r = -EINVAL;
ian@0 1247 goto out_table;
ian@0 1248 }
ian@0 1249
ian@0 1250 ti = dm_table_find_target(table, tmsg->sector);
ian@0 1251 if (ti->type->message)
ian@0 1252 r = ti->type->message(ti, argc, argv);
ian@0 1253 else {
ian@0 1254 DMWARN("Target type does not support messages");
ian@0 1255 r = -EINVAL;
ian@0 1256 }
ian@0 1257
ian@0 1258 out_table:
ian@0 1259 dm_table_put(table);
ian@0 1260 out_argv:
ian@0 1261 kfree(argv);
ian@0 1262 out:
ian@0 1263 param->data_size = 0;
ian@0 1264 dm_put(md);
ian@0 1265 return r;
ian@0 1266 }
ian@0 1267
ian@0 1268 /*-----------------------------------------------------------------
ian@0 1269 * Implementation of open/close/ioctl on the special char
ian@0 1270 * device.
ian@0 1271 *---------------------------------------------------------------*/
ian@0 1272 static ioctl_fn lookup_ioctl(unsigned int cmd)
ian@0 1273 {
ian@0 1274 static struct {
ian@0 1275 int cmd;
ian@0 1276 ioctl_fn fn;
ian@0 1277 } _ioctls[] = {
ian@0 1278 {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */
ian@0 1279 {DM_REMOVE_ALL_CMD, remove_all},
ian@0 1280 {DM_LIST_DEVICES_CMD, list_devices},
ian@0 1281
ian@0 1282 {DM_DEV_CREATE_CMD, dev_create},
ian@0 1283 {DM_DEV_REMOVE_CMD, dev_remove},
ian@0 1284 {DM_DEV_RENAME_CMD, dev_rename},
ian@0 1285 {DM_DEV_SUSPEND_CMD, dev_suspend},
ian@0 1286 {DM_DEV_STATUS_CMD, dev_status},
ian@0 1287 {DM_DEV_WAIT_CMD, dev_wait},
ian@0 1288
ian@0 1289 {DM_TABLE_LOAD_CMD, table_load},
ian@0 1290 {DM_TABLE_CLEAR_CMD, table_clear},
ian@0 1291 {DM_TABLE_DEPS_CMD, table_deps},
ian@0 1292 {DM_TABLE_STATUS_CMD, table_status},
ian@0 1293
ian@0 1294 {DM_LIST_VERSIONS_CMD, list_versions},
ian@0 1295
ian@0 1296 {DM_TARGET_MSG_CMD, target_message},
ian@0 1297 {DM_DEV_SET_GEOMETRY_CMD, dev_set_geometry}
ian@0 1298 };
ian@0 1299
ian@0 1300 return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;
ian@0 1301 }
ian@0 1302
ian@0 1303 /*
ian@0 1304 * As well as checking the version compatibility this always
ian@0 1305 * copies the kernel interface version out.
ian@0 1306 */
ian@0 1307 static int check_version(unsigned int cmd, struct dm_ioctl __user *user)
ian@0 1308 {
ian@0 1309 uint32_t version[3];
ian@0 1310 int r = 0;
ian@0 1311
ian@0 1312 if (copy_from_user(version, user->version, sizeof(version)))
ian@0 1313 return -EFAULT;
ian@0 1314
ian@0 1315 if ((DM_VERSION_MAJOR != version[0]) ||
ian@0 1316 (DM_VERSION_MINOR < version[1])) {
ian@0 1317 DMWARN("ioctl interface mismatch: "
ian@0 1318 "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
ian@0 1319 DM_VERSION_MAJOR, DM_VERSION_MINOR,
ian@0 1320 DM_VERSION_PATCHLEVEL,
ian@0 1321 version[0], version[1], version[2], cmd);
ian@0 1322 r = -EINVAL;
ian@0 1323 }
ian@0 1324
ian@0 1325 /*
ian@0 1326 * Fill in the kernel version.
ian@0 1327 */
ian@0 1328 version[0] = DM_VERSION_MAJOR;
ian@0 1329 version[1] = DM_VERSION_MINOR;
ian@0 1330 version[2] = DM_VERSION_PATCHLEVEL;
ian@0 1331 if (copy_to_user(user->version, version, sizeof(version)))
ian@0 1332 return -EFAULT;
ian@0 1333
ian@0 1334 return r;
ian@0 1335 }
ian@0 1336
ian@0 1337 static void free_params(struct dm_ioctl *param)
ian@0 1338 {
ian@0 1339 vfree(param);
ian@0 1340 }
ian@0 1341
ian@0 1342 static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param)
ian@0 1343 {
ian@0 1344 struct dm_ioctl tmp, *dmi;
ian@0 1345
ian@0 1346 if (copy_from_user(&tmp, user, sizeof(tmp)))
ian@0 1347 return -EFAULT;
ian@0 1348
ian@0 1349 if (tmp.data_size < sizeof(tmp))
ian@0 1350 return -EINVAL;
ian@0 1351
ian@0 1352 dmi = (struct dm_ioctl *) vmalloc(tmp.data_size);
ian@0 1353 if (!dmi)
ian@0 1354 return -ENOMEM;
ian@0 1355
ian@0 1356 if (copy_from_user(dmi, user, tmp.data_size)) {
ian@0 1357 vfree(dmi);
ian@0 1358 return -EFAULT;
ian@0 1359 }
ian@0 1360
ian@0 1361 *param = dmi;
ian@0 1362 return 0;
ian@0 1363 }
ian@0 1364
ian@0 1365 static int validate_params(uint cmd, struct dm_ioctl *param)
ian@0 1366 {
ian@0 1367 /* Always clear this flag */
ian@0 1368 param->flags &= ~DM_BUFFER_FULL_FLAG;
ian@0 1369
ian@0 1370 /* Ignores parameters */
ian@0 1371 if (cmd == DM_REMOVE_ALL_CMD ||
ian@0 1372 cmd == DM_LIST_DEVICES_CMD ||
ian@0 1373 cmd == DM_LIST_VERSIONS_CMD)
ian@0 1374 return 0;
ian@0 1375
ian@0 1376 if ((cmd == DM_DEV_CREATE_CMD)) {
ian@0 1377 if (!*param->name) {
ian@0 1378 DMWARN("name not supplied when creating device");
ian@0 1379 return -EINVAL;
ian@0 1380 }
ian@0 1381 } else if ((*param->uuid && *param->name)) {
ian@0 1382 DMWARN("only supply one of name or uuid, cmd(%u)", cmd);
ian@0 1383 return -EINVAL;
ian@0 1384 }
ian@0 1385
ian@0 1386 /* Ensure strings are terminated */
ian@0 1387 param->name[DM_NAME_LEN - 1] = '\0';
ian@0 1388 param->uuid[DM_UUID_LEN - 1] = '\0';
ian@0 1389
ian@0 1390 return 0;
ian@0 1391 }
ian@0 1392
ian@0 1393 static int ctl_ioctl(struct inode *inode, struct file *file,
ian@0 1394 uint command, ulong u)
ian@0 1395 {
ian@0 1396 int r = 0;
ian@0 1397 unsigned int cmd;
ian@0 1398 struct dm_ioctl *param;
ian@0 1399 struct dm_ioctl __user *user = (struct dm_ioctl __user *) u;
ian@0 1400 ioctl_fn fn = NULL;
ian@0 1401 size_t param_size;
ian@0 1402
ian@0 1403 /* only root can play with this */
ian@0 1404 if (!capable(CAP_SYS_ADMIN))
ian@0 1405 return -EACCES;
ian@0 1406
ian@0 1407 if (_IOC_TYPE(command) != DM_IOCTL)
ian@0 1408 return -ENOTTY;
ian@0 1409
ian@0 1410 cmd = _IOC_NR(command);
ian@0 1411
ian@0 1412 /*
ian@0 1413 * Check the interface version passed in. This also
ian@0 1414 * writes out the kernel's interface version.
ian@0 1415 */
ian@0 1416 r = check_version(cmd, user);
ian@0 1417 if (r)
ian@0 1418 return r;
ian@0 1419
ian@0 1420 /*
ian@0 1421 * Nothing more to do for the version command.
ian@0 1422 */
ian@0 1423 if (cmd == DM_VERSION_CMD)
ian@0 1424 return 0;
ian@0 1425
ian@0 1426 fn = lookup_ioctl(cmd);
ian@0 1427 if (!fn) {
ian@0 1428 DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
ian@0 1429 return -ENOTTY;
ian@0 1430 }
ian@0 1431
ian@0 1432 /*
ian@0 1433 * Trying to avoid low memory issues when a device is
ian@0 1434 * suspended.
ian@0 1435 */
ian@0 1436 current->flags |= PF_MEMALLOC;
ian@0 1437
ian@0 1438 /*
ian@0 1439 * Copy the parameters into kernel space.
ian@0 1440 */
ian@0 1441 r = copy_params(user, &param);
ian@0 1442
ian@0 1443 current->flags &= ~PF_MEMALLOC;
ian@0 1444
ian@0 1445 if (r)
ian@0 1446 return r;
ian@0 1447
ian@0 1448 r = validate_params(cmd, param);
ian@0 1449 if (r)
ian@0 1450 goto out;
ian@0 1451
ian@0 1452 param_size = param->data_size;
ian@0 1453 param->data_size = sizeof(*param);
ian@0 1454 r = fn(param, param_size);
ian@0 1455
ian@0 1456 /*
ian@0 1457 * Copy the results back to userland.
ian@0 1458 */
ian@0 1459 if (!r && copy_to_user(user, param, param->data_size))
ian@0 1460 r = -EFAULT;
ian@0 1461
ian@0 1462 out:
ian@0 1463 free_params(param);
ian@0 1464 return r;
ian@0 1465 }
ian@0 1466
ian@0 1467 static struct file_operations _ctl_fops = {
ian@0 1468 .ioctl = ctl_ioctl,
ian@0 1469 .owner = THIS_MODULE,
ian@0 1470 };
ian@0 1471
ian@0 1472 static struct miscdevice _dm_misc = {
ian@0 1473 .minor = MISC_DYNAMIC_MINOR,
ian@0 1474 .name = DM_NAME,
ian@0 1475 .fops = &_ctl_fops
ian@0 1476 };
ian@0 1477
ian@0 1478 /*
ian@0 1479 * Create misc character device and link to DM_DIR/control.
ian@0 1480 */
ian@0 1481 int __init dm_interface_init(void)
ian@0 1482 {
ian@0 1483 int r;
ian@0 1484
ian@0 1485 r = dm_hash_init();
ian@0 1486 if (r)
ian@0 1487 return r;
ian@0 1488
ian@0 1489 r = misc_register(&_dm_misc);
ian@0 1490 if (r) {
ian@0 1491 DMERR("misc_register failed for control device");
ian@0 1492 dm_hash_exit();
ian@0 1493 return r;
ian@0 1494 }
ian@0 1495
ian@0 1496 DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
ian@0 1497 DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
ian@0 1498 DM_DRIVER_EMAIL);
ian@0 1499 return 0;
ian@0 1500 }
ian@0 1501
ian@0 1502 void dm_interface_exit(void)
ian@0 1503 {
ian@0 1504 if (misc_deregister(&_dm_misc) < 0)
ian@0 1505 DMERR("misc_deregister failed for control device");
ian@0 1506
ian@0 1507 dm_hash_exit();
ian@0 1508 }