| .. | .. |
|---|
| 4 | 4 | * |
|---|
| 5 | 5 | * sysfs attributes. |
|---|
| 6 | 6 | * |
|---|
| 7 | | - * Copyright IBM Corp. 2008, 2010 |
|---|
| 7 | + * Copyright IBM Corp. 2008, 2020 |
|---|
| 8 | 8 | */ |
|---|
| 9 | 9 | |
|---|
| 10 | 10 | #define KMSG_COMPONENT "zfcp" |
|---|
| 11 | 11 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
|---|
| 12 | 12 | |
|---|
| 13 | 13 | #include <linux/slab.h> |
|---|
| 14 | +#include "zfcp_diag.h" |
|---|
| 14 | 15 | #include "zfcp_ext.h" |
|---|
| 15 | 16 | |
|---|
| 16 | 17 | #define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \ |
|---|
| .. | .. |
|---|
| 215 | 216 | { |
|---|
| 216 | 217 | struct ccw_device *cdev = to_ccwdev(dev); |
|---|
| 217 | 218 | struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); |
|---|
| 219 | + int retval = 0; |
|---|
| 218 | 220 | |
|---|
| 219 | 221 | if (!adapter) |
|---|
| 220 | 222 | return -ENODEV; |
|---|
| 223 | + |
|---|
| 224 | + /* |
|---|
| 225 | + * If `scsi_host` is missing, we can't schedule `scan_work`, as it |
|---|
| 226 | + * makes use of the corresponding fc_host object. But this state is |
|---|
| 227 | + * only possible if xconfig/xport data has never completed yet, |
|---|
| 228 | + * and we couldn't successfully scan for ports anyway. |
|---|
| 229 | + */ |
|---|
| 230 | + if (adapter->scsi_host == NULL) { |
|---|
| 231 | + retval = -ENODEV; |
|---|
| 232 | + goto out; |
|---|
| 233 | + } |
|---|
| 221 | 234 | |
|---|
| 222 | 235 | /* |
|---|
| 223 | 236 | * Users wish is our command: immediately schedule and flush a |
|---|
| .. | .. |
|---|
| 226 | 239 | */ |
|---|
| 227 | 240 | queue_delayed_work(adapter->work_queue, &adapter->scan_work, 0); |
|---|
| 228 | 241 | flush_delayed_work(&adapter->scan_work); |
|---|
| 242 | +out: |
|---|
| 229 | 243 | zfcp_ccw_adapter_put(adapter); |
|---|
| 230 | | - |
|---|
| 231 | | - return (ssize_t) count; |
|---|
| 244 | + return retval ? retval : (ssize_t) count; |
|---|
| 232 | 245 | } |
|---|
| 233 | 246 | static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, |
|---|
| 234 | 247 | zfcp_sysfs_port_rescan_store); |
|---|
| .. | .. |
|---|
| 325 | 338 | static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL, |
|---|
| 326 | 339 | zfcp_sysfs_port_remove_store); |
|---|
| 327 | 340 | |
|---|
| 341 | +static ssize_t |
|---|
| 342 | +zfcp_sysfs_adapter_diag_max_age_show(struct device *dev, |
|---|
| 343 | + struct device_attribute *attr, char *buf) |
|---|
| 344 | +{ |
|---|
| 345 | + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); |
|---|
| 346 | + ssize_t rc; |
|---|
| 347 | + |
|---|
| 348 | + if (!adapter) |
|---|
| 349 | + return -ENODEV; |
|---|
| 350 | + |
|---|
| 351 | + /* ceil(log(2^64 - 1) / log(10)) = 20 */ |
|---|
| 352 | + rc = scnprintf(buf, 20 + 2, "%lu\n", adapter->diagnostics->max_age); |
|---|
| 353 | + |
|---|
| 354 | + zfcp_ccw_adapter_put(adapter); |
|---|
| 355 | + return rc; |
|---|
| 356 | +} |
|---|
| 357 | + |
|---|
| 358 | +static ssize_t |
|---|
| 359 | +zfcp_sysfs_adapter_diag_max_age_store(struct device *dev, |
|---|
| 360 | + struct device_attribute *attr, |
|---|
| 361 | + const char *buf, size_t count) |
|---|
| 362 | +{ |
|---|
| 363 | + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); |
|---|
| 364 | + unsigned long max_age; |
|---|
| 365 | + ssize_t rc; |
|---|
| 366 | + |
|---|
| 367 | + if (!adapter) |
|---|
| 368 | + return -ENODEV; |
|---|
| 369 | + |
|---|
| 370 | + rc = kstrtoul(buf, 10, &max_age); |
|---|
| 371 | + if (rc != 0) |
|---|
| 372 | + goto out; |
|---|
| 373 | + |
|---|
| 374 | + adapter->diagnostics->max_age = max_age; |
|---|
| 375 | + |
|---|
| 376 | + rc = count; |
|---|
| 377 | +out: |
|---|
| 378 | + zfcp_ccw_adapter_put(adapter); |
|---|
| 379 | + return rc; |
|---|
| 380 | +} |
|---|
| 381 | +static ZFCP_DEV_ATTR(adapter, diag_max_age, 0644, |
|---|
| 382 | + zfcp_sysfs_adapter_diag_max_age_show, |
|---|
| 383 | + zfcp_sysfs_adapter_diag_max_age_store); |
|---|
| 384 | + |
|---|
| 385 | +static ssize_t zfcp_sysfs_adapter_fc_security_show( |
|---|
| 386 | + struct device *dev, struct device_attribute *attr, char *buf) |
|---|
| 387 | +{ |
|---|
| 388 | + struct ccw_device *cdev = to_ccwdev(dev); |
|---|
| 389 | + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); |
|---|
| 390 | + unsigned int status; |
|---|
| 391 | + int i; |
|---|
| 392 | + |
|---|
| 393 | + if (!adapter) |
|---|
| 394 | + return -ENODEV; |
|---|
| 395 | + |
|---|
| 396 | + /* |
|---|
| 397 | + * Adapter status COMMON_OPEN implies xconf data and xport data |
|---|
| 398 | + * was done. Adapter FC Endpoint Security capability remains |
|---|
| 399 | + * unchanged in case of COMMON_ERP_FAILED (e.g. due to local link |
|---|
| 400 | + * down). |
|---|
| 401 | + */ |
|---|
| 402 | + status = atomic_read(&adapter->status); |
|---|
| 403 | + if (0 == (status & ZFCP_STATUS_COMMON_OPEN)) |
|---|
| 404 | + i = sprintf(buf, "unknown\n"); |
|---|
| 405 | + else if (!(adapter->adapter_features & FSF_FEATURE_FC_SECURITY)) |
|---|
| 406 | + i = sprintf(buf, "unsupported\n"); |
|---|
| 407 | + else { |
|---|
| 408 | + i = zfcp_fsf_scnprint_fc_security( |
|---|
| 409 | + buf, PAGE_SIZE - 1, adapter->fc_security_algorithms, |
|---|
| 410 | + ZFCP_FSF_PRINT_FMT_LIST); |
|---|
| 411 | + i += scnprintf(buf + i, PAGE_SIZE - i, "\n"); |
|---|
| 412 | + } |
|---|
| 413 | + |
|---|
| 414 | + zfcp_ccw_adapter_put(adapter); |
|---|
| 415 | + return i; |
|---|
| 416 | +} |
|---|
| 417 | +static ZFCP_DEV_ATTR(adapter, fc_security, S_IRUGO, |
|---|
| 418 | + zfcp_sysfs_adapter_fc_security_show, |
|---|
| 419 | + NULL); |
|---|
| 420 | + |
|---|
| 328 | 421 | static struct attribute *zfcp_adapter_attrs[] = { |
|---|
| 329 | 422 | &dev_attr_adapter_failed.attr, |
|---|
| 330 | 423 | &dev_attr_adapter_in_recovery.attr, |
|---|
| .. | .. |
|---|
| 337 | 430 | &dev_attr_adapter_lic_version.attr, |
|---|
| 338 | 431 | &dev_attr_adapter_status.attr, |
|---|
| 339 | 432 | &dev_attr_adapter_hardware_version.attr, |
|---|
| 433 | + &dev_attr_adapter_diag_max_age.attr, |
|---|
| 434 | + &dev_attr_adapter_fc_security.attr, |
|---|
| 340 | 435 | NULL |
|---|
| 341 | 436 | }; |
|---|
| 342 | 437 | |
|---|
| .. | .. |
|---|
| 380 | 475 | } |
|---|
| 381 | 476 | static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store); |
|---|
| 382 | 477 | |
|---|
| 478 | +static ssize_t zfcp_sysfs_port_fc_security_show(struct device *dev, |
|---|
| 479 | + struct device_attribute *attr, |
|---|
| 480 | + char *buf) |
|---|
| 481 | +{ |
|---|
| 482 | + struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); |
|---|
| 483 | + struct zfcp_adapter *adapter = port->adapter; |
|---|
| 484 | + unsigned int status = atomic_read(&port->status); |
|---|
| 485 | + int i; |
|---|
| 486 | + |
|---|
| 487 | + if (0 == (status & ZFCP_STATUS_COMMON_OPEN) || |
|---|
| 488 | + 0 == (status & ZFCP_STATUS_COMMON_UNBLOCKED) || |
|---|
| 489 | + 0 == (status & ZFCP_STATUS_PORT_PHYS_OPEN) || |
|---|
| 490 | + 0 != (status & ZFCP_STATUS_PORT_LINK_TEST) || |
|---|
| 491 | + 0 != (status & ZFCP_STATUS_COMMON_ERP_FAILED) || |
|---|
| 492 | + 0 != (status & ZFCP_STATUS_COMMON_ACCESS_BOXED)) |
|---|
| 493 | + i = sprintf(buf, "unknown\n"); |
|---|
| 494 | + else if (!(adapter->adapter_features & FSF_FEATURE_FC_SECURITY)) |
|---|
| 495 | + i = sprintf(buf, "unsupported\n"); |
|---|
| 496 | + else { |
|---|
| 497 | + i = zfcp_fsf_scnprint_fc_security( |
|---|
| 498 | + buf, PAGE_SIZE - 1, port->connection_info, |
|---|
| 499 | + ZFCP_FSF_PRINT_FMT_SINGLEITEM); |
|---|
| 500 | + i += scnprintf(buf + i, PAGE_SIZE - i, "\n"); |
|---|
| 501 | + } |
|---|
| 502 | + |
|---|
| 503 | + return i; |
|---|
| 504 | +} |
|---|
| 505 | +static ZFCP_DEV_ATTR(port, fc_security, S_IRUGO, |
|---|
| 506 | + zfcp_sysfs_port_fc_security_show, |
|---|
| 507 | + NULL); |
|---|
| 508 | + |
|---|
| 383 | 509 | static struct attribute *zfcp_port_attrs[] = { |
|---|
| 384 | 510 | &dev_attr_unit_add.attr, |
|---|
| 385 | 511 | &dev_attr_unit_remove.attr, |
|---|
| .. | .. |
|---|
| 387 | 513 | &dev_attr_port_in_recovery.attr, |
|---|
| 388 | 514 | &dev_attr_port_status.attr, |
|---|
| 389 | 515 | &dev_attr_port_access_denied.attr, |
|---|
| 516 | + &dev_attr_port_fc_security.attr, |
|---|
| 390 | 517 | NULL |
|---|
| 391 | 518 | }; |
|---|
| 392 | 519 | static struct attribute_group zfcp_port_attr_group = { |
|---|
| .. | .. |
|---|
| 577 | 704 | return -ENOMEM; |
|---|
| 578 | 705 | |
|---|
| 579 | 706 | retval = zfcp_fsf_exchange_port_data_sync(adapter->qdio, qtcb_port); |
|---|
| 580 | | - if (!retval) |
|---|
| 707 | + if (retval == 0 || retval == -EAGAIN) |
|---|
| 581 | 708 | retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util, |
|---|
| 582 | 709 | qtcb_port->cb_util, qtcb_port->a_util); |
|---|
| 583 | 710 | kfree(qtcb_port); |
|---|
| .. | .. |
|---|
| 603 | 730 | return -ENOMEM; |
|---|
| 604 | 731 | |
|---|
| 605 | 732 | retval = zfcp_fsf_exchange_config_data_sync(adapter->qdio, qtcb_config); |
|---|
| 606 | | - if (!retval) |
|---|
| 733 | + if (retval == 0 || retval == -EAGAIN) |
|---|
| 607 | 734 | *stat_inf = qtcb_config->stat_info; |
|---|
| 608 | 735 | |
|---|
| 609 | 736 | kfree(qtcb_config); |
|---|
| .. | .. |
|---|
| 664 | 791 | &dev_attr_queue_full, |
|---|
| 665 | 792 | NULL |
|---|
| 666 | 793 | }; |
|---|
| 794 | + |
|---|
| 795 | +static ssize_t zfcp_sysfs_adapter_diag_b2b_credit_show( |
|---|
| 796 | + struct device *dev, struct device_attribute *attr, char *buf) |
|---|
| 797 | +{ |
|---|
| 798 | + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); |
|---|
| 799 | + struct zfcp_diag_header *diag_hdr; |
|---|
| 800 | + struct fc_els_flogi *nsp; |
|---|
| 801 | + ssize_t rc = -ENOLINK; |
|---|
| 802 | + unsigned long flags; |
|---|
| 803 | + unsigned int status; |
|---|
| 804 | + |
|---|
| 805 | + if (!adapter) |
|---|
| 806 | + return -ENODEV; |
|---|
| 807 | + |
|---|
| 808 | + status = atomic_read(&adapter->status); |
|---|
| 809 | + if (0 == (status & ZFCP_STATUS_COMMON_OPEN) || |
|---|
| 810 | + 0 == (status & ZFCP_STATUS_COMMON_UNBLOCKED) || |
|---|
| 811 | + 0 != (status & ZFCP_STATUS_COMMON_ERP_FAILED)) |
|---|
| 812 | + goto out; |
|---|
| 813 | + |
|---|
| 814 | + diag_hdr = &adapter->diagnostics->config_data.header; |
|---|
| 815 | + |
|---|
| 816 | + rc = zfcp_diag_update_buffer_limited( |
|---|
| 817 | + adapter, diag_hdr, zfcp_diag_update_config_data_buffer); |
|---|
| 818 | + if (rc != 0) |
|---|
| 819 | + goto out; |
|---|
| 820 | + |
|---|
| 821 | + spin_lock_irqsave(&diag_hdr->access_lock, flags); |
|---|
| 822 | + /* nport_serv_param doesn't contain the ELS_Command code */ |
|---|
| 823 | + nsp = (struct fc_els_flogi *)((unsigned long) |
|---|
| 824 | + adapter->diagnostics->config_data |
|---|
| 825 | + .data.nport_serv_param - |
|---|
| 826 | + sizeof(u32)); |
|---|
| 827 | + |
|---|
| 828 | + rc = scnprintf(buf, 5 + 2, "%hu\n", |
|---|
| 829 | + be16_to_cpu(nsp->fl_csp.sp_bb_cred)); |
|---|
| 830 | + spin_unlock_irqrestore(&diag_hdr->access_lock, flags); |
|---|
| 831 | + |
|---|
| 832 | +out: |
|---|
| 833 | + zfcp_ccw_adapter_put(adapter); |
|---|
| 834 | + return rc; |
|---|
| 835 | +} |
|---|
| 836 | +static ZFCP_DEV_ATTR(adapter_diag, b2b_credit, 0400, |
|---|
| 837 | + zfcp_sysfs_adapter_diag_b2b_credit_show, NULL); |
|---|
| 838 | + |
|---|
| 839 | +#define ZFCP_DEFINE_DIAG_SFP_ATTR(_name, _qtcb_member, _prtsize, _prtfmt) \ |
|---|
| 840 | + static ssize_t zfcp_sysfs_adapter_diag_sfp_##_name##_show( \ |
|---|
| 841 | + struct device *dev, struct device_attribute *attr, char *buf) \ |
|---|
| 842 | + { \ |
|---|
| 843 | + struct zfcp_adapter *const adapter = \ |
|---|
| 844 | + zfcp_ccw_adapter_by_cdev(to_ccwdev(dev)); \ |
|---|
| 845 | + struct zfcp_diag_header *diag_hdr; \ |
|---|
| 846 | + ssize_t rc = -ENOLINK; \ |
|---|
| 847 | + unsigned long flags; \ |
|---|
| 848 | + unsigned int status; \ |
|---|
| 849 | + \ |
|---|
| 850 | + if (!adapter) \ |
|---|
| 851 | + return -ENODEV; \ |
|---|
| 852 | + \ |
|---|
| 853 | + status = atomic_read(&adapter->status); \ |
|---|
| 854 | + if (0 == (status & ZFCP_STATUS_COMMON_OPEN) || \ |
|---|
| 855 | + 0 == (status & ZFCP_STATUS_COMMON_UNBLOCKED) || \ |
|---|
| 856 | + 0 != (status & ZFCP_STATUS_COMMON_ERP_FAILED)) \ |
|---|
| 857 | + goto out; \ |
|---|
| 858 | + \ |
|---|
| 859 | + if (!zfcp_diag_support_sfp(adapter)) { \ |
|---|
| 860 | + rc = -EOPNOTSUPP; \ |
|---|
| 861 | + goto out; \ |
|---|
| 862 | + } \ |
|---|
| 863 | + \ |
|---|
| 864 | + diag_hdr = &adapter->diagnostics->port_data.header; \ |
|---|
| 865 | + \ |
|---|
| 866 | + rc = zfcp_diag_update_buffer_limited( \ |
|---|
| 867 | + adapter, diag_hdr, zfcp_diag_update_port_data_buffer); \ |
|---|
| 868 | + if (rc != 0) \ |
|---|
| 869 | + goto out; \ |
|---|
| 870 | + \ |
|---|
| 871 | + spin_lock_irqsave(&diag_hdr->access_lock, flags); \ |
|---|
| 872 | + rc = scnprintf( \ |
|---|
| 873 | + buf, (_prtsize) + 2, _prtfmt "\n", \ |
|---|
| 874 | + adapter->diagnostics->port_data.data._qtcb_member); \ |
|---|
| 875 | + spin_unlock_irqrestore(&diag_hdr->access_lock, flags); \ |
|---|
| 876 | + \ |
|---|
| 877 | + out: \ |
|---|
| 878 | + zfcp_ccw_adapter_put(adapter); \ |
|---|
| 879 | + return rc; \ |
|---|
| 880 | + } \ |
|---|
| 881 | + static ZFCP_DEV_ATTR(adapter_diag_sfp, _name, 0400, \ |
|---|
| 882 | + zfcp_sysfs_adapter_diag_sfp_##_name##_show, NULL) |
|---|
| 883 | + |
|---|
| 884 | +ZFCP_DEFINE_DIAG_SFP_ATTR(temperature, temperature, 6, "%hd"); |
|---|
| 885 | +ZFCP_DEFINE_DIAG_SFP_ATTR(vcc, vcc, 5, "%hu"); |
|---|
| 886 | +ZFCP_DEFINE_DIAG_SFP_ATTR(tx_bias, tx_bias, 5, "%hu"); |
|---|
| 887 | +ZFCP_DEFINE_DIAG_SFP_ATTR(tx_power, tx_power, 5, "%hu"); |
|---|
| 888 | +ZFCP_DEFINE_DIAG_SFP_ATTR(rx_power, rx_power, 5, "%hu"); |
|---|
| 889 | +ZFCP_DEFINE_DIAG_SFP_ATTR(port_tx_type, sfp_flags.port_tx_type, 2, "%hu"); |
|---|
| 890 | +ZFCP_DEFINE_DIAG_SFP_ATTR(optical_port, sfp_flags.optical_port, 1, "%hu"); |
|---|
| 891 | +ZFCP_DEFINE_DIAG_SFP_ATTR(sfp_invalid, sfp_flags.sfp_invalid, 1, "%hu"); |
|---|
| 892 | +ZFCP_DEFINE_DIAG_SFP_ATTR(connector_type, sfp_flags.connector_type, 1, "%hu"); |
|---|
| 893 | +ZFCP_DEFINE_DIAG_SFP_ATTR(fec_active, sfp_flags.fec_active, 1, "%hu"); |
|---|
| 894 | + |
|---|
| 895 | +static struct attribute *zfcp_sysfs_diag_attrs[] = { |
|---|
| 896 | + &dev_attr_adapter_diag_sfp_temperature.attr, |
|---|
| 897 | + &dev_attr_adapter_diag_sfp_vcc.attr, |
|---|
| 898 | + &dev_attr_adapter_diag_sfp_tx_bias.attr, |
|---|
| 899 | + &dev_attr_adapter_diag_sfp_tx_power.attr, |
|---|
| 900 | + &dev_attr_adapter_diag_sfp_rx_power.attr, |
|---|
| 901 | + &dev_attr_adapter_diag_sfp_port_tx_type.attr, |
|---|
| 902 | + &dev_attr_adapter_diag_sfp_optical_port.attr, |
|---|
| 903 | + &dev_attr_adapter_diag_sfp_sfp_invalid.attr, |
|---|
| 904 | + &dev_attr_adapter_diag_sfp_connector_type.attr, |
|---|
| 905 | + &dev_attr_adapter_diag_sfp_fec_active.attr, |
|---|
| 906 | + &dev_attr_adapter_diag_b2b_credit.attr, |
|---|
| 907 | + NULL, |
|---|
| 908 | +}; |
|---|
| 909 | + |
|---|
| 910 | +const struct attribute_group zfcp_sysfs_diag_attr_group = { |
|---|
| 911 | + .name = "diagnostics", |
|---|
| 912 | + .attrs = zfcp_sysfs_diag_attrs, |
|---|
| 913 | +}; |
|---|