diff options
Diffstat (limited to 'drivers/s390/scsi/zfcp_aux.c')
-rw-r--r-- | drivers/s390/scsi/zfcp_aux.c | 425 |
1 files changed, 29 insertions, 396 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 9eb8827e19e2..735f7af43d6a 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c | |||
@@ -33,6 +33,7 @@ | |||
33 | * Ralph Wuerthner | 33 | * Ralph Wuerthner |
34 | */ | 34 | */ |
35 | 35 | ||
36 | #include <linux/miscdevice.h> | ||
36 | #include "zfcp_ext.h" | 37 | #include "zfcp_ext.h" |
37 | 38 | ||
38 | /* accumulated log level (module parameter) */ | 39 | /* accumulated log level (module parameter) */ |
@@ -43,33 +44,6 @@ static char *device; | |||
43 | /* written against the module interface */ | 44 | /* written against the module interface */ |
44 | static int __init zfcp_module_init(void); | 45 | static int __init zfcp_module_init(void); |
45 | 46 | ||
46 | /* miscellaneous */ | ||
47 | static int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t); | ||
48 | static void zfcp_sg_list_free(struct zfcp_sg_list *); | ||
49 | static int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *, | ||
50 | void __user *, size_t); | ||
51 | static int zfcp_sg_list_copy_to_user(void __user *, | ||
52 | struct zfcp_sg_list *, size_t); | ||
53 | static long zfcp_cfdc_dev_ioctl(struct file *, unsigned int, unsigned long); | ||
54 | |||
55 | #define ZFCP_CFDC_IOC_MAGIC 0xDD | ||
56 | #define ZFCP_CFDC_IOC \ | ||
57 | _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_sense_data) | ||
58 | |||
59 | |||
60 | static const struct file_operations zfcp_cfdc_fops = { | ||
61 | .unlocked_ioctl = zfcp_cfdc_dev_ioctl, | ||
62 | #ifdef CONFIG_COMPAT | ||
63 | .compat_ioctl = zfcp_cfdc_dev_ioctl | ||
64 | #endif | ||
65 | }; | ||
66 | |||
67 | static struct miscdevice zfcp_cfdc_misc = { | ||
68 | .minor = ZFCP_CFDC_DEV_MINOR, | ||
69 | .name = ZFCP_CFDC_DEV_NAME, | ||
70 | .fops = &zfcp_cfdc_fops | ||
71 | }; | ||
72 | |||
73 | /*********************** KERNEL/MODULE PARAMETERS ***************************/ | 47 | /*********************** KERNEL/MODULE PARAMETERS ***************************/ |
74 | 48 | ||
75 | /* declare driver module init/cleanup functions */ | 49 | /* declare driver module init/cleanup functions */ |
@@ -294,9 +268,6 @@ zfcp_module_init(void) | |||
294 | goto out_misc; | 268 | goto out_misc; |
295 | } | 269 | } |
296 | 270 | ||
297 | ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n", | ||
298 | ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor); | ||
299 | |||
300 | /* Initialise proc semaphores */ | 271 | /* Initialise proc semaphores */ |
301 | sema_init(&zfcp_data.config_sema, 1); | 272 | sema_init(&zfcp_data.config_sema, 1); |
302 | 273 | ||
@@ -329,372 +300,6 @@ zfcp_module_init(void) | |||
329 | return retval; | 300 | return retval; |
330 | } | 301 | } |
331 | 302 | ||
332 | /* | ||
333 | * function: zfcp_cfdc_dev_ioctl | ||
334 | * | ||
335 | * purpose: Handle control file upload/download transaction via IOCTL | ||
336 | * interface | ||
337 | * | ||
338 | * returns: 0 - Operation completed successfuly | ||
339 | * -ENOTTY - Unknown IOCTL command | ||
340 | * -EINVAL - Invalid sense data record | ||
341 | * -ENXIO - The FCP adapter is not available | ||
342 | * -EOPNOTSUPP - The FCP adapter does not have CFDC support | ||
343 | * -ENOMEM - Insufficient memory | ||
344 | * -EFAULT - User space memory I/O operation fault | ||
345 | * -EPERM - Cannot create or queue FSF request or create SBALs | ||
346 | * -ERESTARTSYS- Received signal (is mapped to EAGAIN by VFS) | ||
347 | */ | ||
348 | static long | ||
349 | zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command, | ||
350 | unsigned long buffer) | ||
351 | { | ||
352 | struct zfcp_cfdc_sense_data *sense_data, __user *sense_data_user; | ||
353 | struct zfcp_adapter *adapter = NULL; | ||
354 | struct zfcp_fsf_req *fsf_req = NULL; | ||
355 | struct zfcp_sg_list *sg_list = NULL; | ||
356 | u32 fsf_command, option; | ||
357 | char *bus_id = NULL; | ||
358 | int retval = 0; | ||
359 | |||
360 | sense_data = kmalloc(sizeof(struct zfcp_cfdc_sense_data), GFP_KERNEL); | ||
361 | if (sense_data == NULL) { | ||
362 | retval = -ENOMEM; | ||
363 | goto out; | ||
364 | } | ||
365 | |||
366 | sg_list = kzalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL); | ||
367 | if (sg_list == NULL) { | ||
368 | retval = -ENOMEM; | ||
369 | goto out; | ||
370 | } | ||
371 | |||
372 | if (command != ZFCP_CFDC_IOC) { | ||
373 | ZFCP_LOG_INFO("IOC request code 0x%x invalid\n", command); | ||
374 | retval = -ENOTTY; | ||
375 | goto out; | ||
376 | } | ||
377 | |||
378 | if ((sense_data_user = (void __user *) buffer) == NULL) { | ||
379 | ZFCP_LOG_INFO("sense data record is required\n"); | ||
380 | retval = -EINVAL; | ||
381 | goto out; | ||
382 | } | ||
383 | |||
384 | retval = copy_from_user(sense_data, sense_data_user, | ||
385 | sizeof(struct zfcp_cfdc_sense_data)); | ||
386 | if (retval) { | ||
387 | retval = -EFAULT; | ||
388 | goto out; | ||
389 | } | ||
390 | |||
391 | if (sense_data->signature != ZFCP_CFDC_SIGNATURE) { | ||
392 | ZFCP_LOG_INFO("invalid sense data request signature 0x%08x\n", | ||
393 | ZFCP_CFDC_SIGNATURE); | ||
394 | retval = -EINVAL; | ||
395 | goto out; | ||
396 | } | ||
397 | |||
398 | switch (sense_data->command) { | ||
399 | |||
400 | case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL: | ||
401 | fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; | ||
402 | option = FSF_CFDC_OPTION_NORMAL_MODE; | ||
403 | break; | ||
404 | |||
405 | case ZFCP_CFDC_CMND_DOWNLOAD_FORCE: | ||
406 | fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; | ||
407 | option = FSF_CFDC_OPTION_FORCE; | ||
408 | break; | ||
409 | |||
410 | case ZFCP_CFDC_CMND_FULL_ACCESS: | ||
411 | fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; | ||
412 | option = FSF_CFDC_OPTION_FULL_ACCESS; | ||
413 | break; | ||
414 | |||
415 | case ZFCP_CFDC_CMND_RESTRICTED_ACCESS: | ||
416 | fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; | ||
417 | option = FSF_CFDC_OPTION_RESTRICTED_ACCESS; | ||
418 | break; | ||
419 | |||
420 | case ZFCP_CFDC_CMND_UPLOAD: | ||
421 | fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE; | ||
422 | option = 0; | ||
423 | break; | ||
424 | |||
425 | default: | ||
426 | ZFCP_LOG_INFO("invalid command code 0x%08x\n", | ||
427 | sense_data->command); | ||
428 | retval = -EINVAL; | ||
429 | goto out; | ||
430 | } | ||
431 | |||
432 | bus_id = kmalloc(BUS_ID_SIZE, GFP_KERNEL); | ||
433 | if (bus_id == NULL) { | ||
434 | retval = -ENOMEM; | ||
435 | goto out; | ||
436 | } | ||
437 | snprintf(bus_id, BUS_ID_SIZE, "%d.%d.%04x", | ||
438 | (sense_data->devno >> 24), | ||
439 | (sense_data->devno >> 16) & 0xFF, | ||
440 | (sense_data->devno & 0xFFFF)); | ||
441 | |||
442 | read_lock_irq(&zfcp_data.config_lock); | ||
443 | adapter = zfcp_get_adapter_by_busid(bus_id); | ||
444 | if (adapter) | ||
445 | zfcp_adapter_get(adapter); | ||
446 | read_unlock_irq(&zfcp_data.config_lock); | ||
447 | |||
448 | kfree(bus_id); | ||
449 | |||
450 | if (adapter == NULL) { | ||
451 | ZFCP_LOG_INFO("invalid adapter\n"); | ||
452 | retval = -ENXIO; | ||
453 | goto out; | ||
454 | } | ||
455 | |||
456 | if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) { | ||
457 | retval = zfcp_sg_list_alloc(sg_list, | ||
458 | ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); | ||
459 | if (retval) { | ||
460 | retval = -ENOMEM; | ||
461 | goto out; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | if ((sense_data->command & ZFCP_CFDC_DOWNLOAD) && | ||
466 | (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE)) { | ||
467 | retval = zfcp_sg_list_copy_from_user( | ||
468 | sg_list, &sense_data_user->control_file, | ||
469 | ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); | ||
470 | if (retval) { | ||
471 | retval = -EFAULT; | ||
472 | goto out; | ||
473 | } | ||
474 | } | ||
475 | |||
476 | retval = zfcp_fsf_control_file(adapter, &fsf_req, fsf_command, | ||
477 | option, sg_list); | ||
478 | if (retval) | ||
479 | goto out; | ||
480 | |||
481 | if ((fsf_req->qtcb->prefix.prot_status != FSF_PROT_GOOD) && | ||
482 | (fsf_req->qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) { | ||
483 | retval = -ENXIO; | ||
484 | goto out; | ||
485 | } | ||
486 | |||
487 | sense_data->fsf_status = fsf_req->qtcb->header.fsf_status; | ||
488 | memcpy(&sense_data->fsf_status_qual, | ||
489 | &fsf_req->qtcb->header.fsf_status_qual, | ||
490 | sizeof(union fsf_status_qual)); | ||
491 | memcpy(&sense_data->payloads, &fsf_req->qtcb->bottom.support.els, 256); | ||
492 | |||
493 | retval = copy_to_user(sense_data_user, sense_data, | ||
494 | sizeof(struct zfcp_cfdc_sense_data)); | ||
495 | if (retval) { | ||
496 | retval = -EFAULT; | ||
497 | goto out; | ||
498 | } | ||
499 | |||
500 | if (sense_data->command & ZFCP_CFDC_UPLOAD) { | ||
501 | retval = zfcp_sg_list_copy_to_user( | ||
502 | &sense_data_user->control_file, sg_list, | ||
503 | ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); | ||
504 | if (retval) { | ||
505 | retval = -EFAULT; | ||
506 | goto out; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | out: | ||
511 | if (fsf_req != NULL) | ||
512 | zfcp_fsf_req_free(fsf_req); | ||
513 | |||
514 | if ((adapter != NULL) && (retval != -ENXIO)) | ||
515 | zfcp_adapter_put(adapter); | ||
516 | |||
517 | if (sg_list != NULL) { | ||
518 | zfcp_sg_list_free(sg_list); | ||
519 | kfree(sg_list); | ||
520 | } | ||
521 | |||
522 | kfree(sense_data); | ||
523 | |||
524 | return retval; | ||
525 | } | ||
526 | |||
527 | |||
528 | /** | ||
529 | * zfcp_sg_list_alloc - create a scatter-gather list of the specified size | ||
530 | * @sg_list: structure describing a scatter gather list | ||
531 | * @size: size of scatter-gather list | ||
532 | * Return: 0 on success, else -ENOMEM | ||
533 | * | ||
534 | * In sg_list->sg a pointer to the created scatter-gather list is returned, | ||
535 | * or NULL if we run out of memory. sg_list->count specifies the number of | ||
536 | * elements of the scatter-gather list. The maximum size of a single element | ||
537 | * in the scatter-gather list is PAGE_SIZE. | ||
538 | */ | ||
539 | static int | ||
540 | zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) | ||
541 | { | ||
542 | struct scatterlist *sg; | ||
543 | unsigned int i; | ||
544 | int retval = 0; | ||
545 | void *address; | ||
546 | |||
547 | BUG_ON(sg_list == NULL); | ||
548 | |||
549 | sg_list->count = size >> PAGE_SHIFT; | ||
550 | if (size & ~PAGE_MASK) | ||
551 | sg_list->count++; | ||
552 | sg_list->sg = kcalloc(sg_list->count, sizeof(struct scatterlist), | ||
553 | GFP_KERNEL); | ||
554 | if (sg_list->sg == NULL) { | ||
555 | sg_list->count = 0; | ||
556 | retval = -ENOMEM; | ||
557 | goto out; | ||
558 | } | ||
559 | sg_init_table(sg_list->sg, sg_list->count); | ||
560 | |||
561 | for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) { | ||
562 | address = (void *) get_zeroed_page(GFP_KERNEL); | ||
563 | if (address == NULL) { | ||
564 | sg_list->count = i; | ||
565 | zfcp_sg_list_free(sg_list); | ||
566 | retval = -ENOMEM; | ||
567 | goto out; | ||
568 | } | ||
569 | zfcp_address_to_sg(address, sg, min(size, PAGE_SIZE)); | ||
570 | size -= sg->length; | ||
571 | } | ||
572 | |||
573 | out: | ||
574 | return retval; | ||
575 | } | ||
576 | |||
577 | |||
578 | /** | ||
579 | * zfcp_sg_list_free - free memory of a scatter-gather list | ||
580 | * @sg_list: structure describing a scatter-gather list | ||
581 | * | ||
582 | * Memory for each element in the scatter-gather list is freed. | ||
583 | * Finally sg_list->sg is freed itself and sg_list->count is reset. | ||
584 | */ | ||
585 | static void | ||
586 | zfcp_sg_list_free(struct zfcp_sg_list *sg_list) | ||
587 | { | ||
588 | struct scatterlist *sg; | ||
589 | unsigned int i; | ||
590 | |||
591 | BUG_ON(sg_list == NULL); | ||
592 | |||
593 | for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) | ||
594 | free_page((unsigned long) zfcp_sg_to_address(sg)); | ||
595 | |||
596 | sg_list->count = 0; | ||
597 | kfree(sg_list->sg); | ||
598 | } | ||
599 | |||
600 | /** | ||
601 | * zfcp_sg_size - determine size of a scatter-gather list | ||
602 | * @sg: array of (struct scatterlist) | ||
603 | * @sg_count: elements in array | ||
604 | * Return: size of entire scatter-gather list | ||
605 | */ | ||
606 | static size_t zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count) | ||
607 | { | ||
608 | unsigned int i; | ||
609 | struct scatterlist *p; | ||
610 | size_t size; | ||
611 | |||
612 | size = 0; | ||
613 | for (i = 0, p = sg; i < sg_count; i++, p++) { | ||
614 | BUG_ON(p == NULL); | ||
615 | size += p->length; | ||
616 | } | ||
617 | |||
618 | return size; | ||
619 | } | ||
620 | |||
621 | |||
622 | /** | ||
623 | * zfcp_sg_list_copy_from_user -copy data from user space to scatter-gather list | ||
624 | * @sg_list: structure describing a scatter-gather list | ||
625 | * @user_buffer: pointer to buffer in user space | ||
626 | * @size: number of bytes to be copied | ||
627 | * Return: 0 on success, -EFAULT if copy_from_user fails. | ||
628 | */ | ||
629 | static int | ||
630 | zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, | ||
631 | void __user *user_buffer, | ||
632 | size_t size) | ||
633 | { | ||
634 | struct scatterlist *sg; | ||
635 | unsigned int length; | ||
636 | void *zfcp_buffer; | ||
637 | int retval = 0; | ||
638 | |||
639 | BUG_ON(sg_list == NULL); | ||
640 | |||
641 | if (zfcp_sg_size(sg_list->sg, sg_list->count) < size) | ||
642 | return -EFAULT; | ||
643 | |||
644 | for (sg = sg_list->sg; size > 0; sg++) { | ||
645 | length = min((unsigned int)size, sg->length); | ||
646 | zfcp_buffer = zfcp_sg_to_address(sg); | ||
647 | if (copy_from_user(zfcp_buffer, user_buffer, length)) { | ||
648 | retval = -EFAULT; | ||
649 | goto out; | ||
650 | } | ||
651 | user_buffer += length; | ||
652 | size -= length; | ||
653 | } | ||
654 | |||
655 | out: | ||
656 | return retval; | ||
657 | } | ||
658 | |||
659 | |||
660 | /** | ||
661 | * zfcp_sg_list_copy_to_user - copy data from scatter-gather list to user space | ||
662 | * @user_buffer: pointer to buffer in user space | ||
663 | * @sg_list: structure describing a scatter-gather list | ||
664 | * @size: number of bytes to be copied | ||
665 | * Return: 0 on success, -EFAULT if copy_to_user fails | ||
666 | */ | ||
667 | static int | ||
668 | zfcp_sg_list_copy_to_user(void __user *user_buffer, | ||
669 | struct zfcp_sg_list *sg_list, | ||
670 | size_t size) | ||
671 | { | ||
672 | struct scatterlist *sg; | ||
673 | unsigned int length; | ||
674 | void *zfcp_buffer; | ||
675 | int retval = 0; | ||
676 | |||
677 | BUG_ON(sg_list == NULL); | ||
678 | |||
679 | if (zfcp_sg_size(sg_list->sg, sg_list->count) < size) | ||
680 | return -EFAULT; | ||
681 | |||
682 | for (sg = sg_list->sg; size > 0; sg++) { | ||
683 | length = min((unsigned int) size, sg->length); | ||
684 | zfcp_buffer = zfcp_sg_to_address(sg); | ||
685 | if (copy_to_user(user_buffer, zfcp_buffer, length)) { | ||
686 | retval = -EFAULT; | ||
687 | goto out; | ||
688 | } | ||
689 | user_buffer += length; | ||
690 | size -= length; | ||
691 | } | ||
692 | |||
693 | out: | ||
694 | return retval; | ||
695 | } | ||
696 | |||
697 | |||
698 | #undef ZFCP_LOG_AREA | 303 | #undef ZFCP_LOG_AREA |
699 | 304 | ||
700 | /****************************************************************/ | 305 | /****************************************************************/ |
@@ -1345,4 +950,32 @@ zfcp_nameserver_enqueue(struct zfcp_adapter *adapter) | |||
1345 | return 0; | 950 | return 0; |
1346 | } | 951 | } |
1347 | 952 | ||
953 | void zfcp_sg_free_table(struct scatterlist *sg, int count) | ||
954 | { | ||
955 | int i; | ||
956 | |||
957 | for (i = 0; i < count; i++, sg++) | ||
958 | if (sg) | ||
959 | free_page((unsigned long) sg_virt(sg)); | ||
960 | else | ||
961 | break; | ||
962 | } | ||
963 | |||
964 | int zfcp_sg_setup_table(struct scatterlist *sg, int count) | ||
965 | { | ||
966 | void *addr; | ||
967 | int i; | ||
968 | |||
969 | sg_init_table(sg, count); | ||
970 | for (i = 0; i < count; i++, sg++) { | ||
971 | addr = (void *) get_zeroed_page(GFP_KERNEL); | ||
972 | if (!addr) { | ||
973 | zfcp_sg_free_table(sg, i); | ||
974 | return -ENOMEM; | ||
975 | } | ||
976 | sg_set_buf(sg, addr, PAGE_SIZE); | ||
977 | } | ||
978 | return 0; | ||
979 | } | ||
980 | |||
1348 | #undef ZFCP_LOG_AREA | 981 | #undef ZFCP_LOG_AREA |