diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_attr.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 0de69324212e..9496e87c135e 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c | |||
@@ -551,6 +551,119 @@ static CLASS_DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, | |||
551 | lpfc_board_mode_show, lpfc_board_mode_store); | 551 | lpfc_board_mode_show, lpfc_board_mode_store); |
552 | static CLASS_DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); | 552 | static CLASS_DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); |
553 | 553 | ||
554 | |||
555 | static char *lpfc_soft_wwpn_key = "C99G71SL8032A"; | ||
556 | |||
557 | static ssize_t | ||
558 | lpfc_soft_wwpn_enable_store(struct class_device *cdev, const char *buf, | ||
559 | size_t count) | ||
560 | { | ||
561 | struct Scsi_Host *host = class_to_shost(cdev); | ||
562 | struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; | ||
563 | unsigned int cnt = count; | ||
564 | |||
565 | /* | ||
566 | * We're doing a simple sanity check for soft_wwpn setting. | ||
567 | * We require that the user write a specific key to enable | ||
568 | * the soft_wwpn attribute to be settable. Once the attribute | ||
569 | * is written, the enable key resets. If further updates are | ||
570 | * desired, the key must be written again to re-enable the | ||
571 | * attribute. | ||
572 | * | ||
573 | * The "key" is not secret - it is a hardcoded string shown | ||
574 | * here. The intent is to protect against the random user or | ||
575 | * application that is just writing attributes. | ||
576 | */ | ||
577 | |||
578 | /* count may include a LF at end of string */ | ||
579 | if (buf[cnt-1] == '\n') | ||
580 | cnt--; | ||
581 | |||
582 | if ((cnt != strlen(lpfc_soft_wwpn_key)) || | ||
583 | (strncmp(buf, lpfc_soft_wwpn_key, strlen(lpfc_soft_wwpn_key)) != 0)) | ||
584 | return -EINVAL; | ||
585 | |||
586 | phba->soft_wwpn_enable = 1; | ||
587 | return count; | ||
588 | } | ||
589 | static CLASS_DEVICE_ATTR(lpfc_soft_wwpn_enable, S_IWUSR, NULL, | ||
590 | lpfc_soft_wwpn_enable_store); | ||
591 | |||
592 | static ssize_t | ||
593 | lpfc_soft_wwpn_show(struct class_device *cdev, char *buf) | ||
594 | { | ||
595 | struct Scsi_Host *host = class_to_shost(cdev); | ||
596 | struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; | ||
597 | return snprintf(buf, PAGE_SIZE, "0x%llx\n", phba->cfg_soft_wwpn); | ||
598 | } | ||
599 | |||
600 | |||
601 | static ssize_t | ||
602 | lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count) | ||
603 | { | ||
604 | struct Scsi_Host *host = class_to_shost(cdev); | ||
605 | struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; | ||
606 | struct completion online_compl; | ||
607 | int stat1=0, stat2=0; | ||
608 | unsigned int i, j, cnt=count; | ||
609 | u8 wwpn[8]; | ||
610 | |||
611 | /* count may include a LF at end of string */ | ||
612 | if (buf[cnt-1] == '\n') | ||
613 | cnt--; | ||
614 | |||
615 | if (!phba->soft_wwpn_enable || (cnt < 16) || (cnt > 18) || | ||
616 | ((cnt == 17) && (*buf++ != 'x')) || | ||
617 | ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x')))) | ||
618 | return -EINVAL; | ||
619 | |||
620 | phba->soft_wwpn_enable = 0; | ||
621 | |||
622 | memset(wwpn, 0, sizeof(wwpn)); | ||
623 | |||
624 | /* Validate and store the new name */ | ||
625 | for (i=0, j=0; i < 16; i++) { | ||
626 | if ((*buf >= 'a') && (*buf <= 'f')) | ||
627 | j = ((j << 4) | ((*buf++ -'a') + 10)); | ||
628 | else if ((*buf >= 'A') && (*buf <= 'F')) | ||
629 | j = ((j << 4) | ((*buf++ -'A') + 10)); | ||
630 | else if ((*buf >= '0') && (*buf <= '9')) | ||
631 | j = ((j << 4) | (*buf++ -'0')); | ||
632 | else | ||
633 | return -EINVAL; | ||
634 | if (i % 2) { | ||
635 | wwpn[i/2] = j & 0xff; | ||
636 | j = 0; | ||
637 | } | ||
638 | } | ||
639 | phba->cfg_soft_wwpn = wwn_to_u64(wwpn); | ||
640 | fc_host_port_name(host) = phba->cfg_soft_wwpn; | ||
641 | |||
642 | dev_printk(KERN_NOTICE, &phba->pcidev->dev, | ||
643 | "lpfc%d: Reinitializing to use soft_wwpn\n", phba->brd_no); | ||
644 | |||
645 | init_completion(&online_compl); | ||
646 | lpfc_workq_post_event(phba, &stat1, &online_compl, LPFC_EVT_OFFLINE); | ||
647 | wait_for_completion(&online_compl); | ||
648 | if (stat1) | ||
649 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
650 | "%d:0463 lpfc_soft_wwpn attribute set failed to reinit " | ||
651 | "adapter - %d\n", phba->brd_no, stat1); | ||
652 | |||
653 | init_completion(&online_compl); | ||
654 | lpfc_workq_post_event(phba, &stat2, &online_compl, LPFC_EVT_ONLINE); | ||
655 | wait_for_completion(&online_compl); | ||
656 | if (stat2) | ||
657 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
658 | "%d:0464 lpfc_soft_wwpn attribute set failed to reinit " | ||
659 | "adapter - %d\n", phba->brd_no, stat2); | ||
660 | |||
661 | return (stat1 || stat2) ? -EIO : count; | ||
662 | } | ||
663 | static CLASS_DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR,\ | ||
664 | lpfc_soft_wwpn_show, lpfc_soft_wwpn_store); | ||
665 | |||
666 | |||
554 | static int lpfc_poll = 0; | 667 | static int lpfc_poll = 0; |
555 | module_param(lpfc_poll, int, 0); | 668 | module_param(lpfc_poll, int, 0); |
556 | MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:" | 669 | MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:" |
@@ -832,6 +945,7 @@ LPFC_ATTR_R(max_luns, 255, 0, 65535, | |||
832 | LPFC_ATTR_RW(poll_tmo, 10, 1, 255, | 945 | LPFC_ATTR_RW(poll_tmo, 10, 1, 255, |
833 | "Milliseconds driver will wait between polling FCP ring"); | 946 | "Milliseconds driver will wait between polling FCP ring"); |
834 | 947 | ||
948 | |||
835 | struct class_device_attribute *lpfc_host_attrs[] = { | 949 | struct class_device_attribute *lpfc_host_attrs[] = { |
836 | &class_device_attr_info, | 950 | &class_device_attr_info, |
837 | &class_device_attr_serialnum, | 951 | &class_device_attr_serialnum, |
@@ -867,6 +981,8 @@ struct class_device_attribute *lpfc_host_attrs[] = { | |||
867 | &class_device_attr_issue_reset, | 981 | &class_device_attr_issue_reset, |
868 | &class_device_attr_lpfc_poll, | 982 | &class_device_attr_lpfc_poll, |
869 | &class_device_attr_lpfc_poll_tmo, | 983 | &class_device_attr_lpfc_poll_tmo, |
984 | &class_device_attr_lpfc_soft_wwpn, | ||
985 | &class_device_attr_lpfc_soft_wwpn_enable, | ||
870 | NULL, | 986 | NULL, |
871 | }; | 987 | }; |
872 | 988 | ||
@@ -1668,6 +1784,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) | |||
1668 | lpfc_devloss_tmo_init(phba, lpfc_devloss_tmo); | 1784 | lpfc_devloss_tmo_init(phba, lpfc_devloss_tmo); |
1669 | lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo); | 1785 | lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo); |
1670 | phba->cfg_poll = lpfc_poll; | 1786 | phba->cfg_poll = lpfc_poll; |
1787 | phba->cfg_soft_wwpn = 0L; | ||
1671 | 1788 | ||
1672 | /* | 1789 | /* |
1673 | * The total number of segments is the configuration value plus 2 | 1790 | * The total number of segments is the configuration value plus 2 |