diff options
author | Christof Schmitt <christof.schmitt@de.ibm.com> | 2008-06-10 12:20:55 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-12 09:22:25 -0400 |
commit | 45633fdc9615f9fd2a0ae18e301562298b15abf3 (patch) | |
tree | 8a91c7fffaf55d484c333443735572b4fb0c0a48 /drivers/s390/scsi/zfcp_aux.c | |
parent | 24073b475d6d2bad8880434a16343ee1da816ea5 (diff) |
[SCSI] zfcp: Move CFDC code to new file.
zfcp implements a device file to allow Linux guests changing the
Access Control Tables stored in the adapter. The code for the device
file has nothing to do with the other parts of the driver, so move it
to a new file and cleanup the code while doing so.
Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com>
Signed-off-by: Swen Schillig <swen@vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
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 |