From: Swen Schillig <swen@xxxxxxxxxxxx> add statistics and other zfcp related information to sysfs The zfcp adapter provides a variety of information which was never published to the external world. This patch adds a few of those "statistics" to the sysfs tree structure. These are reflected in the following attributes /sys/bus/ccw/drivers/zfcp/0.0.1707 /* information for the virtual adapter */ statistic_services/ requests megabytes utilization seconds_active /* LUN specific info for channel- and fabric-latency */ 0x500507630300c562/0x401040a600000000/ read_latency write_latency cmd_latency Signed-off-by: Swen Schillig <swen@xxxxxxxxxxxx> --- drivers/s390/scsi/Makefile | 2 drivers/s390/scsi/zfcp_aux.c | 30 ++++- drivers/s390/scsi/zfcp_def.h | 14 ++ drivers/s390/scsi/zfcp_ext.h | 2 drivers/s390/scsi/zfcp_fsf.c | 1 drivers/s390/scsi/zfcp_fsf.h | 28 ++++- drivers/s390/scsi/zfcp_qdio.c | 39 +++++++ drivers/s390/scsi/zfcp_sysfs_statistics.c | 162 ++++++++++++++++++++++++++++++ drivers/s390/scsi/zfcp_sysfs_unit.c | 63 +++++++++++ 9 files changed, 333 insertions(+), 8 deletions(-) Index: HEAD/drivers/s390/scsi/zfcp_def.h =================================================================== --- HEAD.orig/drivers/s390/scsi/zfcp_def.h +++ HEAD/drivers/s390/scsi/zfcp_def.h @@ -868,6 +868,17 @@ struct zfcp_erp_action { struct timer_list timer; }; +struct latency_cont { + u32 channel; + u32 fabric; + u32 counter; +}; + +struct zfcp_latencies { + struct latency_cont read; + struct latency_cont write; + struct latency_cont cmd; +}; struct zfcp_adapter { struct list_head list; /* list of adapters */ @@ -883,6 +894,7 @@ struct zfcp_adapter { u32 adapter_features; /* FCP channel features */ u32 connection_features; /* host connection features */ u32 hardware_version; /* of FCP channel */ + u16 timer_ticks; /* time int for a tick */ struct Scsi_Host *scsi_host; /* Pointer to mid-layer */ struct list_head port_list_head; /* remote port list */ struct list_head port_remove_lh; /* head of ports to be @@ -930,6 +942,7 @@ struct zfcp_adapter { struct zfcp_scsi_dbf_record scsi_dbf_buf; struct zfcp_adapter_mempool pool; /* Adapter memory pools */ struct qdio_initialize qdio_init_data; /* for qdio_establish */ + struct device stat_services; struct device generic_services; /* directory for WKA ports */ struct fc_host_statistics *fc_stats; struct fsf_qtcb_bottom_port *stats_reset_data; @@ -986,6 +999,7 @@ struct zfcp_unit { all scsi_scan_target requests have been completed. */ + struct zfcp_latencies latencies; }; /* FSF request */ Index: HEAD/drivers/s390/scsi/zfcp_fsf.c =================================================================== --- HEAD.orig/drivers/s390/scsi/zfcp_fsf.c +++ HEAD/drivers/s390/scsi/zfcp_fsf.c @@ -2079,6 +2079,7 @@ zfcp_fsf_exchange_config_evaluate(struct fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3; adapter->hydra_version = bottom->adapter_type; + adapter->timer_ticks = bottom->timer_interval; if (fc_host_permanent_port_name(shost) == -1) fc_host_permanent_port_name(shost) = fc_host_port_name(shost); Index: HEAD/drivers/s390/scsi/zfcp_fsf.h =================================================================== --- HEAD.orig/drivers/s390/scsi/zfcp_fsf.h +++ HEAD/drivers/s390/scsi/zfcp_fsf.h @@ -322,11 +322,18 @@ struct fsf_link_down_info { u8 vendor_specific_code; } __attribute__ ((packed)); +struct fsf_qual_latency_info { + u32 channel_lat; + u32 fabric_lat; + u8 res1[8]; +} __attribute__ ((packed)); + union fsf_prot_status_qual { u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)]; struct fsf_qual_version_error version_error; struct fsf_qual_sequence_error sequence_error; struct fsf_link_down_info link_down_info; + struct fsf_qual_latency_info latency_info; } __attribute__ ((packed)); struct fsf_qtcb_prefix { @@ -340,6 +347,15 @@ struct fsf_qtcb_prefix { u8 res1[20]; } __attribute__ ((packed)); +struct fsf_statistics_info { + u64 input_req; + u64 output_req; + u64 control_req; + u64 input_mb; + u64 output_mb; + u64 seconds_act; +} __attribute__ ((packed)); + union fsf_status_qual { u8 byte[FSF_STATUS_QUALIFIER_SIZE]; u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)]; @@ -427,7 +443,9 @@ struct fsf_qtcb_bottom_config { u32 fc_link_speed; u32 adapter_type; u32 peer_d_id; - u8 res2[12]; + u8 res1[2]; + u16 timer_interval; + u8 res2[8]; u32 s_id; struct fsf_nport_serv_param nport_serv_param; u8 reserved_nport_serv_param[16]; @@ -436,7 +454,8 @@ struct fsf_qtcb_bottom_config { u32 hardware_version; u8 serial_number[32]; struct fsf_nport_serv_param plogi_payload; - u8 res4[160]; + struct fsf_statistics_info stat_info; + u8 res4[112]; } __attribute__ ((packed)); struct fsf_qtcb_bottom_port { @@ -469,7 +488,10 @@ struct fsf_qtcb_bottom_port { u64 control_requests; u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */ u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */ - u8 res2[256]; + u8 cp_util; + u8 cb_util; + u8 a_util; + u8 res2[253]; } __attribute__ ((packed)); union fsf_qtcb_bottom { Index: HEAD/drivers/s390/scsi/zfcp_qdio.c =================================================================== --- HEAD.orig/drivers/s390/scsi/zfcp_qdio.c +++ HEAD/drivers/s390/scsi/zfcp_qdio.c @@ -232,6 +232,40 @@ zfcp_qdio_request_handler(struct ccw_dev return; } +static void zfcp_qdio_fsf_req_latency(struct zfcp_fsf_req *fsf_req) +{ + struct fsf_qual_latency_info *lat_inf; + struct zfcp_unit *unit; + + lat_inf = &fsf_req->qtcb->prefix.prot_status_qual.latency_info; + unit = fsf_req->unit; + + switch (fsf_req->qtcb->bottom.io.data_direction) { + case FSF_DATADIR_READ: + unit->latencies.read.channel += + lat_inf->channel_lat; + unit->latencies.read.fabric += + lat_inf->fabric_lat; + unit->latencies.read.counter++; + break; + case FSF_DATADIR_WRITE: + unit->latencies.write.channel += + lat_inf->channel_lat; + unit->latencies.write.fabric += + lat_inf->fabric_lat; + unit->latencies.write.counter++; + break; + case FSF_DATADIR_CMND: + unit->latencies.cmd.channel += + lat_inf->channel_lat; + unit->latencies.cmd.fabric += + lat_inf->fabric_lat; + unit->latencies.cmd.counter++; + break; + } +} + + /** * zfcp_qdio_reqid_check - checks for valid reqids. */ @@ -254,6 +288,11 @@ static void zfcp_qdio_reqid_check(struct panic("error: unknown request id (%ld) on adapter %s.\n", req_id, zfcp_get_busid_by_adapter(adapter)); + if ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) && + (fsf_req->qtcb->prefix.prot_status & + (FSF_PROT_GOOD | FSF_PROT_FSF_STATUS_PRESENTED))) + zfcp_qdio_fsf_req_latency(fsf_req); + zfcp_reqlist_remove(adapter, fsf_req); atomic_dec(&adapter->reqs_active); spin_unlock_irqrestore(&adapter->req_list_lock, flags); Index: HEAD/drivers/s390/scsi/zfcp_sysfs_unit.c =================================================================== --- HEAD.orig/drivers/s390/scsi/zfcp_sysfs_unit.c +++ HEAD/drivers/s390/scsi/zfcp_sysfs_unit.c @@ -125,6 +125,66 @@ zfcp_sysfs_unit_failed_show(struct devic static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_unit_failed_show, zfcp_sysfs_unit_failed_store); +static ssize_t +zfcp_sysfs_unit_read_latency_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct zfcp_unit *unit; + struct zfcp_latencies *lat; + struct zfcp_adapter *adapter; + + unit = dev_get_drvdata(dev); + lat = &unit->latencies; + adapter = unit->port->adapter; + + return sprintf(buf, "%u %u %u\n", + lat->read.fabric * adapter->timer_ticks / 1000, + lat->read.channel * adapter->timer_ticks / 1000, + lat->read.counter); +} + +static DEVICE_ATTR(read_latency, S_IRUGO, zfcp_sysfs_unit_read_latency_show, + NULL); + +static ssize_t +zfcp_sysfs_unit_write_latency_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct zfcp_unit *unit; + struct zfcp_latencies *lat; + struct zfcp_adapter *adapter; + + unit = dev_get_drvdata(dev); + lat = &unit->latencies; + adapter = unit->port->adapter; + + return sprintf(buf, "%u %u %u\n", + lat->write.fabric * adapter->timer_ticks / 1000, + lat->write.channel * adapter->timer_ticks / 1000, + lat->write.counter); +} + +static DEVICE_ATTR(write_latency, S_IRUGO, zfcp_sysfs_unit_write_latency_show, + NULL); + +static ssize_t +zfcp_sysfs_unit_cmd_latency_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct zfcp_unit *unit; + struct zfcp_latencies *lat; + struct zfcp_adapter *adapter; + + unit = dev_get_drvdata(dev); + lat = &unit->latencies; + adapter = unit->port->adapter; + + return sprintf(buf, "%u %u %u\n", + lat->cmd.fabric * adapter->timer_ticks / 1000, + lat->cmd.channel * adapter->timer_ticks / 1000, + lat->cmd.counter); +} + +static DEVICE_ATTR(cmd_latency, S_IRUGO, zfcp_sysfs_unit_cmd_latency_show, + NULL); + static struct attribute *zfcp_unit_attrs[] = { &dev_attr_failed.attr, &dev_attr_in_recovery.attr, @@ -132,6 +192,9 @@ static struct attribute *zfcp_unit_attrs &dev_attr_access_denied.attr, &dev_attr_access_shared.attr, &dev_attr_access_readonly.attr, + &dev_attr_read_latency.attr, + &dev_attr_write_latency.attr, + &dev_attr_cmd_latency.attr, NULL }; Index: HEAD/drivers/s390/scsi/zfcp_ext.h =================================================================== --- HEAD.orig/drivers/s390/scsi/zfcp_ext.h +++ HEAD/drivers/s390/scsi/zfcp_ext.h @@ -37,6 +37,8 @@ extern int zfcp_sysfs_unit_create_files extern void zfcp_sysfs_unit_remove_files(struct device *); extern void zfcp_sysfs_port_release(struct device *); extern void zfcp_sysfs_unit_release(struct device *); +extern int zfcp_sysfs_statistic_services_create_files(struct device *); +extern void zfcp_sysfs_statistic_services_remove_files(struct device *); /**************************** CONFIGURATION *********************************/ extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, fcp_lun_t); Index: HEAD/drivers/s390/scsi/zfcp_aux.c =================================================================== --- HEAD.orig/drivers/s390/scsi/zfcp_aux.c +++ HEAD/drivers/s390/scsi/zfcp_aux.c @@ -1067,13 +1067,32 @@ zfcp_adapter_enqueue(struct ccw_device * if (zfcp_sysfs_adapter_create_files(&ccw_device->dev)) goto sysfs_failed; + adapter->stat_services.parent = &adapter->ccw_device->dev; + adapter->stat_services.release = zfcp_dummy_release; + snprintf(adapter->stat_services.bus_id, BUS_ID_SIZE, + "statistic_services"); + dev_set_drvdata(&adapter->stat_services, adapter); + + if (device_register(&adapter->stat_services)) + goto services_failed; + + if (zfcp_sysfs_statistic_services_create_files(&adapter->stat_services)) + { + device_unregister(&adapter->stat_services); + goto services_failed; + } + adapter->generic_services.parent = &adapter->ccw_device->dev; adapter->generic_services.release = zfcp_dummy_release; snprintf(adapter->generic_services.bus_id, BUS_ID_SIZE, "generic_services"); - if (device_register(&adapter->generic_services)) - goto generic_services_failed; + if (device_register(&adapter->generic_services)) { + zfcp_sysfs_statistic_services_remove_files(&adapter-> + stat_services); + device_unregister(&adapter->stat_services); + goto services_failed; + } /* put allocated adapter at list tail */ write_lock_irq(&zfcp_data.config_lock); @@ -1085,7 +1104,7 @@ zfcp_adapter_enqueue(struct ccw_device * goto out; - generic_services_failed: + services_failed: zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); sysfs_failed: zfcp_adapter_debug_unregister(adapter); @@ -1118,9 +1137,11 @@ zfcp_adapter_dequeue(struct zfcp_adapter int retval = 0; unsigned long flags; - zfcp_adapter_scsi_unregister(adapter); device_unregister(&adapter->generic_services); + zfcp_sysfs_statistic_services_remove_files(&adapter->stat_services); + device_unregister(&adapter->stat_services); zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); + zfcp_adapter_scsi_unregister(adapter); dev_set_drvdata(&adapter->ccw_device->dev, NULL); /* sanity check: no pending FSF requests */ spin_lock_irqsave(&adapter->req_list_lock, flags); @@ -1264,6 +1285,7 @@ zfcp_port_enqueue(struct zfcp_adapter *a if (zfcp_sysfs_port_create_files(&port->sysfs_device, status)) { device_unregister(&port->sysfs_device); + kfree(port); return NULL; } Index: HEAD/drivers/s390/scsi/Makefile =================================================================== --- HEAD.orig/drivers/s390/scsi/Makefile +++ HEAD/drivers/s390/scsi/Makefile @@ -4,6 +4,6 @@ zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \ zfcp_fsf.o zfcp_dbf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \ - zfcp_sysfs_unit.o zfcp_sysfs_driver.o + zfcp_sysfs_unit.o zfcp_sysfs_driver.o zfcp_sysfs_statistics.o obj-$(CONFIG_ZFCP) += zfcp.o Index: HEAD/drivers/s390/scsi/zfcp_sysfs_statistics.c =================================================================== --- /dev/null +++ HEAD/drivers/s390/scsi/zfcp_sysfs_statistics.c @@ -0,0 +1,162 @@ +/* + * This file is part of the zfcp device driver for + * FCP adapters for IBM System z9 and zSeries. + * + * Copyright IBM Corp. 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "zfcp_ext.h" + +#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG + + +static ssize_t +zfcp_sysfs_adapter_utilization_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zfcp_adapter *adapter; + struct fsf_qtcb_bottom_port *qtcb_port; + int retval; + + adapter = dev_get_drvdata(dev); + + qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL); + if (!qtcb_port) + return -ENOMEM; + + retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port); + if (!retval) + retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util, + qtcb_port->cb_util, qtcb_port->a_util); + kfree(qtcb_port); + return retval; +} + +static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_utilization_show, + NULL); + +static ssize_t +zfcp_sysfs_adapter_request_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zfcp_adapter *adapter; + struct fsf_qtcb_bottom_config *qtcb_config; + int retval; + + adapter = dev_get_drvdata(dev); + + qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config), + GFP_KERNEL); + if (!qtcb_config) + return -ENOMEM; + + retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config); + if (!retval) + retval = sprintf(buf, "%lu %lu %lu\n", + qtcb_config->stat_info.input_req, + qtcb_config->stat_info.output_req, + qtcb_config->stat_info.control_req); + + kfree(qtcb_config); + return retval; +} + +static DEVICE_ATTR(requests, S_IRUGO, zfcp_sysfs_adapter_request_show, NULL); + +static ssize_t +zfcp_sysfs_adapter_mb_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zfcp_adapter *adapter; + struct fsf_qtcb_bottom_config *qtcb_config; + int retval; + + adapter = dev_get_drvdata(dev); + + qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config), + GFP_KERNEL); + + if (!qtcb_config) + return -ENOMEM; + + retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config); + if (!retval) + retval = sprintf(buf, "%lu %lu\n", + qtcb_config->stat_info.input_mb, + qtcb_config->stat_info.output_mb); + + kfree(qtcb_config); + return retval; +} + +static DEVICE_ATTR(megabytes, S_IRUGO, zfcp_sysfs_adapter_mb_show, NULL); + +static ssize_t +zfcp_sysfs_adapter_seconds_active_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct zfcp_adapter *adapter; + struct fsf_qtcb_bottom_config *qtcb_config; + int retval; + + adapter = dev_get_drvdata(dev); + + qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config), + GFP_KERNEL); + + if (!qtcb_config) + return -ENOMEM; + + retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config); + if (!retval) + retval = sprintf(buf, "%lu\n", + qtcb_config->stat_info.seconds_act); + + kfree(qtcb_config); + return retval; +} + +static DEVICE_ATTR(seconds_active, S_IRUGO, + zfcp_sysfs_adapter_seconds_active_show, NULL); + +static struct attribute *zfcp_statistic_services_attrs[] = { + &dev_attr_utilization.attr, + &dev_attr_requests.attr, + &dev_attr_megabytes.attr, + &dev_attr_seconds_active.attr, + NULL +}; + +static struct attribute_group zfcp_statistic_services_attr_group = { + .attrs = zfcp_statistic_services_attrs, +}; + +int +zfcp_sysfs_statistic_services_create_files(struct device *dev) +{ + return sysfs_create_group(&dev->kobj, + &zfcp_statistic_services_attr_group); +} + +void +zfcp_sysfs_statistic_services_remove_files(struct device *dev) +{ + sysfs_remove_group(&dev->kobj, &zfcp_statistic_services_attr_group); +} + +#undef ZFCP_LOG_AREA - To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html