diff options
Diffstat (limited to 'arch/um/drivers/ubd_kern.c')
-rw-r--r-- | arch/um/drivers/ubd_kern.c | 132 |
1 files changed, 74 insertions, 58 deletions
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 49c047b75cc5..f4db97efc014 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c | |||
@@ -286,7 +286,7 @@ static int parse_unit(char **ptr) | |||
286 | * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it | 286 | * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it |
287 | * should not be freed on exit. | 287 | * should not be freed on exit. |
288 | */ | 288 | */ |
289 | static int ubd_setup_common(char *str, int *index_out) | 289 | static int ubd_setup_common(char *str, int *index_out, char **error_out) |
290 | { | 290 | { |
291 | struct ubd *ubd_dev; | 291 | struct ubd *ubd_dev; |
292 | struct openflags flags = global_openflags; | 292 | struct openflags flags = global_openflags; |
@@ -302,56 +302,54 @@ static int ubd_setup_common(char *str, int *index_out) | |||
302 | str++; | 302 | str++; |
303 | if(!strcmp(str, "sync")){ | 303 | if(!strcmp(str, "sync")){ |
304 | global_openflags = of_sync(global_openflags); | 304 | global_openflags = of_sync(global_openflags); |
305 | return(0); | 305 | return 0; |
306 | } | 306 | } |
307 | major = simple_strtoul(str, &end, 0); | 307 | major = simple_strtoul(str, &end, 0); |
308 | if((*end != '\0') || (end == str)){ | 308 | if((*end != '\0') || (end == str)){ |
309 | printk(KERN_ERR | 309 | *error_out = "Didn't parse major number"; |
310 | "ubd_setup : didn't parse major number\n"); | 310 | return -EINVAL; |
311 | return(1); | ||
312 | } | 311 | } |
313 | 312 | ||
314 | err = 1; | 313 | err = -EINVAL; |
315 | mutex_lock(&ubd_lock); | 314 | mutex_lock(&ubd_lock); |
316 | if(fake_major != MAJOR_NR){ | 315 | if(fake_major != MAJOR_NR){ |
317 | printk(KERN_ERR "Can't assign a fake major twice\n"); | 316 | *error_out = "Can't assign a fake major twice"; |
318 | goto out1; | 317 | goto out1; |
319 | } | 318 | } |
320 | 319 | ||
321 | fake_major = major; | 320 | fake_major = major; |
322 | 321 | ||
323 | printk(KERN_INFO "Setting extra ubd major number to %d\n", | 322 | printk(KERN_INFO "Setting extra ubd major number to %d\n", |
324 | major); | 323 | major); |
325 | err = 0; | 324 | err = 0; |
326 | out1: | 325 | out1: |
327 | mutex_unlock(&ubd_lock); | 326 | mutex_unlock(&ubd_lock); |
328 | return(err); | 327 | return err; |
329 | } | 328 | } |
330 | 329 | ||
331 | n = parse_unit(&str); | 330 | n = parse_unit(&str); |
332 | if(n < 0){ | 331 | if(n < 0){ |
333 | printk(KERN_ERR "ubd_setup : couldn't parse unit number " | 332 | *error_out = "Couldn't parse device number"; |
334 | "'%s'\n", str); | 333 | return -EINVAL; |
335 | return(1); | ||
336 | } | 334 | } |
337 | if(n >= MAX_DEV){ | 335 | if(n >= MAX_DEV){ |
338 | printk(KERN_ERR "ubd_setup : index %d out of range " | 336 | *error_out = "Device number out of range"; |
339 | "(%d devices, from 0 to %d)\n", n, MAX_DEV, MAX_DEV - 1); | 337 | return 1; |
340 | return(1); | ||
341 | } | 338 | } |
342 | 339 | ||
343 | err = 1; | 340 | err = -EBUSY; |
344 | mutex_lock(&ubd_lock); | 341 | mutex_lock(&ubd_lock); |
345 | 342 | ||
346 | ubd_dev = &ubd_devs[n]; | 343 | ubd_dev = &ubd_devs[n]; |
347 | if(ubd_dev->file != NULL){ | 344 | if(ubd_dev->file != NULL){ |
348 | printk(KERN_ERR "ubd_setup : device already configured\n"); | 345 | *error_out = "Device is already configured"; |
349 | goto out; | 346 | goto out; |
350 | } | 347 | } |
351 | 348 | ||
352 | if (index_out) | 349 | if (index_out) |
353 | *index_out = n; | 350 | *index_out = n; |
354 | 351 | ||
352 | err = -EINVAL; | ||
355 | for (i = 0; i < sizeof("rscd="); i++) { | 353 | for (i = 0; i < sizeof("rscd="); i++) { |
356 | switch (*str) { | 354 | switch (*str) { |
357 | case 'r': | 355 | case 'r': |
@@ -370,47 +368,54 @@ static int ubd_setup_common(char *str, int *index_out) | |||
370 | str++; | 368 | str++; |
371 | goto break_loop; | 369 | goto break_loop; |
372 | default: | 370 | default: |
373 | printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r, s, c, or d)\n"); | 371 | *error_out = "Expected '=' or flag letter " |
372 | "(r, s, c, or d)"; | ||
374 | goto out; | 373 | goto out; |
375 | } | 374 | } |
376 | str++; | 375 | str++; |
377 | } | 376 | } |
378 | 377 | ||
379 | if (*str == '=') | 378 | if (*str == '=') |
380 | printk(KERN_ERR "ubd_setup : Too many flags specified\n"); | 379 | *error_out = "Too many flags specified"; |
381 | else | 380 | else |
382 | printk(KERN_ERR "ubd_setup : Expected '='\n"); | 381 | *error_out = "Missing '='"; |
383 | goto out; | 382 | goto out; |
384 | 383 | ||
385 | break_loop: | 384 | break_loop: |
386 | err = 0; | ||
387 | backing_file = strchr(str, ','); | 385 | backing_file = strchr(str, ','); |
388 | 386 | ||
389 | if (!backing_file) { | 387 | if (backing_file == NULL) |
390 | backing_file = strchr(str, ':'); | 388 | backing_file = strchr(str, ':'); |
391 | } | ||
392 | 389 | ||
393 | if(backing_file){ | 390 | if(backing_file != NULL){ |
394 | if(ubd_dev->no_cow) | 391 | if(ubd_dev->no_cow){ |
395 | printk(KERN_ERR "Can't specify both 'd' and a " | 392 | *error_out = "Can't specify both 'd' and a cow file"; |
396 | "cow file\n"); | 393 | goto out; |
394 | } | ||
397 | else { | 395 | else { |
398 | *backing_file = '\0'; | 396 | *backing_file = '\0'; |
399 | backing_file++; | 397 | backing_file++; |
400 | } | 398 | } |
401 | } | 399 | } |
400 | err = 0; | ||
402 | ubd_dev->file = str; | 401 | ubd_dev->file = str; |
403 | ubd_dev->cow.file = backing_file; | 402 | ubd_dev->cow.file = backing_file; |
404 | ubd_dev->boot_openflags = flags; | 403 | ubd_dev->boot_openflags = flags; |
405 | out: | 404 | out: |
406 | mutex_unlock(&ubd_lock); | 405 | mutex_unlock(&ubd_lock); |
407 | return(err); | 406 | return err; |
408 | } | 407 | } |
409 | 408 | ||
410 | static int ubd_setup(char *str) | 409 | static int ubd_setup(char *str) |
411 | { | 410 | { |
412 | ubd_setup_common(str, NULL); | 411 | char *error; |
413 | return(1); | 412 | int err; |
413 | |||
414 | err = ubd_setup_common(str, NULL, &error); | ||
415 | if(err) | ||
416 | printk(KERN_ERR "Failed to initialize device with \"%s\" : " | ||
417 | "%s\n", str, error); | ||
418 | return 1; | ||
414 | } | 419 | } |
415 | 420 | ||
416 | __setup("ubd", ubd_setup); | 421 | __setup("ubd", ubd_setup); |
@@ -422,7 +427,7 @@ __uml_help(ubd_setup, | |||
422 | " use either a ':' or a ',': the first one allows writing things like;\n" | 427 | " use either a ':' or a ',': the first one allows writing things like;\n" |
423 | " ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n" | 428 | " ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n" |
424 | " while with a ',' the shell would not expand the 2nd '~'.\n" | 429 | " while with a ',' the shell would not expand the 2nd '~'.\n" |
425 | " When using only one filename, UML will detect whether to thread it like\n" | 430 | " When using only one filename, UML will detect whether to treat it like\n" |
426 | " a COW file or a backing file. To override this detection, add the 'd'\n" | 431 | " a COW file or a backing file. To override this detection, add the 'd'\n" |
427 | " flag:\n" | 432 | " flag:\n" |
428 | " ubd0d=BackingFile\n" | 433 | " ubd0d=BackingFile\n" |
@@ -668,18 +673,19 @@ static int ubd_disk_register(int major, u64 size, int unit, | |||
668 | 673 | ||
669 | #define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9)) | 674 | #define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9)) |
670 | 675 | ||
671 | static int ubd_add(int n) | 676 | static int ubd_add(int n, char **error_out) |
672 | { | 677 | { |
673 | struct ubd *ubd_dev = &ubd_devs[n]; | 678 | struct ubd *ubd_dev = &ubd_devs[n]; |
674 | int err; | 679 | int err = 0; |
675 | 680 | ||
676 | err = -ENODEV; | ||
677 | if(ubd_dev->file == NULL) | 681 | if(ubd_dev->file == NULL) |
678 | goto out; | 682 | goto out; |
679 | 683 | ||
680 | err = ubd_file_size(ubd_dev, &ubd_dev->size); | 684 | err = ubd_file_size(ubd_dev, &ubd_dev->size); |
681 | if(err < 0) | 685 | if(err < 0){ |
686 | *error_out = "Couldn't determine size of device's file"; | ||
682 | goto out; | 687 | goto out; |
688 | } | ||
683 | 689 | ||
684 | ubd_dev->size = ROUND_BLOCK(ubd_dev->size); | 690 | ubd_dev->size = ROUND_BLOCK(ubd_dev->size); |
685 | 691 | ||
@@ -701,28 +707,31 @@ out: | |||
701 | return err; | 707 | return err; |
702 | } | 708 | } |
703 | 709 | ||
704 | static int ubd_config(char *str) | 710 | static int ubd_config(char *str, char **error_out) |
705 | { | 711 | { |
706 | int n, ret; | 712 | int n, ret; |
707 | 713 | ||
714 | /* This string is possibly broken up and stored, so it's only | ||
715 | * freed if ubd_setup_common fails, or if only general options | ||
716 | * were set. | ||
717 | */ | ||
708 | str = kstrdup(str, GFP_KERNEL); | 718 | str = kstrdup(str, GFP_KERNEL); |
709 | if (str == NULL) { | 719 | if (str == NULL) { |
710 | printk(KERN_ERR "ubd_config failed to strdup string\n"); | 720 | *error_out = "Failed to allocate memory"; |
711 | ret = 1; | 721 | return -ENOMEM; |
712 | goto out; | ||
713 | } | 722 | } |
714 | ret = ubd_setup_common(str, &n); | 723 | |
715 | if (ret) { | 724 | ret = ubd_setup_common(str, &n, error_out); |
716 | ret = -1; | 725 | if (ret) |
717 | goto err_free; | 726 | goto err_free; |
718 | } | 727 | |
719 | if (n == -1) { | 728 | if (n == -1) { |
720 | ret = 0; | 729 | ret = 0; |
721 | goto err_free; | 730 | goto err_free; |
722 | } | 731 | } |
723 | 732 | ||
724 | mutex_lock(&ubd_lock); | 733 | mutex_lock(&ubd_lock); |
725 | ret = ubd_add(n); | 734 | ret = ubd_add(n, error_out); |
726 | if (ret) | 735 | if (ret) |
727 | ubd_devs[n].file = NULL; | 736 | ubd_devs[n].file = NULL; |
728 | mutex_unlock(&ubd_lock); | 737 | mutex_unlock(&ubd_lock); |
@@ -777,7 +786,7 @@ static int ubd_id(char **str, int *start_out, int *end_out) | |||
777 | return n; | 786 | return n; |
778 | } | 787 | } |
779 | 788 | ||
780 | static int ubd_remove(int n) | 789 | static int ubd_remove(int n, char **error_out) |
781 | { | 790 | { |
782 | struct ubd *ubd_dev; | 791 | struct ubd *ubd_dev; |
783 | int err = -ENODEV; | 792 | int err = -ENODEV; |
@@ -815,7 +824,9 @@ out: | |||
815 | return err; | 824 | return err; |
816 | } | 825 | } |
817 | 826 | ||
818 | /* All these are called by mconsole in process context and without ubd-specific locks. */ | 827 | /* All these are called by mconsole in process context and without |
828 | * ubd-specific locks. | ||
829 | */ | ||
819 | static struct mc_device ubd_mc = { | 830 | static struct mc_device ubd_mc = { |
820 | .name = "ubd", | 831 | .name = "ubd", |
821 | .config = ubd_config, | 832 | .config = ubd_config, |
@@ -851,7 +862,8 @@ static struct platform_driver ubd_driver = { | |||
851 | 862 | ||
852 | static int __init ubd_init(void) | 863 | static int __init ubd_init(void) |
853 | { | 864 | { |
854 | int i; | 865 | char *error; |
866 | int i, err; | ||
855 | 867 | ||
856 | if (register_blkdev(MAJOR_NR, "ubd")) | 868 | if (register_blkdev(MAJOR_NR, "ubd")) |
857 | return -1; | 869 | return -1; |
@@ -870,8 +882,12 @@ static int __init ubd_init(void) | |||
870 | return -1; | 882 | return -1; |
871 | } | 883 | } |
872 | platform_driver_register(&ubd_driver); | 884 | platform_driver_register(&ubd_driver); |
873 | for (i = 0; i < MAX_DEV; i++) | 885 | for (i = 0; i < MAX_DEV; i++){ |
874 | ubd_add(i); | 886 | err = ubd_add(i, &error); |
887 | if(err) | ||
888 | printk(KERN_ERR "Failed to initialize ubd device %d :" | ||
889 | "%s\n", i, error); | ||
890 | } | ||
875 | return 0; | 891 | return 0; |
876 | } | 892 | } |
877 | 893 | ||