From: Hans-Joachim Picht <hpicht@redhat.com> Date: Thu, 12 Mar 2009 15:25:16 +0100 Subject: [s390] add FCP performance data collection Message-id: 20090312142516.GL5103@redhat.com O-Subject: [RHEL5 U4 PATCH 11/20] FEAT: s390 : Add FCP performance data collection Bugzilla: 475334 Bugzilla: ========= BZ 475334 https://bugzilla.redhat.com/show_bug.cgi?id=475334 diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index ad4a0d5..cfe5153 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -1109,6 +1109,7 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) spin_lock_init(&adapter->san_dbf_lock); spin_lock_init(&adapter->scsi_dbf_lock); spin_lock_init(&adapter->rec_dbf_lock); + spin_lock_init(&adapter->qdio_stat_lock); /* initialize error recovery stuff */ diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 6aae76d..ef94c67 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -32,6 +32,7 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/timer.h> +#include <linux/ktime.h> #include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_cmnd.h> @@ -926,6 +927,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 @@ -979,6 +981,10 @@ struct zfcp_adapter { struct fsf_qtcb_bottom_port *stats_reset_data; unsigned long stats_reset; struct service_level service_level; + ktime_t req_q_time; /* time of last fill level change */ + u64 req_q_util; /* for accounting */ + spinlock_t qdio_stat_lock; + atomic_t qdio_outb_full; /* queue full incidents */ }; /* @@ -1060,6 +1066,8 @@ struct zfcp_fsf_req { from emergency pool */ unsigned long long issued; /* request sent time (STCK) */ struct zfcp_unit *unit; + u16 qdio_outb_usage;/* usage of outbound queue */ + u16 qdio_inb_usage; /* usage of inbound queue */ }; typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*); diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 1556ff1..d59afb5 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -209,4 +209,6 @@ extern int zfcp_reqlist_isempty(struct zfcp_adapter *); extern void set_fc_internal_target_scan(struct scsi_transport_template *t); +extern struct class_device_attribute *zfcp_sysfs_shost_attrs[]; + #endif /* ZFCP_EXT_H */ diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 7d9477a..646cfa6 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/blktrace_api.h> #include "zfcp_ext.h" static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *); @@ -1931,6 +1932,7 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) fc_host_speed(shost) = bottom->fc_link_speed; 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); @@ -3734,6 +3736,36 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) return retval; } +#ifdef CONFIG_BLK_DEV_IO_TRACE +static void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req) +{ + struct fsf_qual_latency_info *meas; + struct scsi_cmnd *scsi_cmnd = (struct scsi_cmnd *)fsf_req->data; + struct request *req = scsi_cmnd->request; + struct zfcp_blk_drv_data trace; + int ticks = fsf_req->adapter->timer_ticks; + + trace.flags = 0; + trace.magic = ZFCP_BLK_DRV_DATA_MAGIC; + if (fsf_req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) { + trace.flags |= ZFCP_BLK_LAT_VALID; + meas = &fsf_req->qtcb->prefix.prot_status_qual.latency_info; + trace.channel_lat = meas->channel_lat * ticks; + trace.fabric_lat = meas->fabric_lat * ticks; + } + if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) + trace.flags |= ZFCP_BLK_REQ_ERROR; + trace.inb_usage = fsf_req->qdio_inb_usage; + trace.outb_usage = fsf_req->qdio_outb_usage; + + blk_add_driver_data(req->q, req, &trace, sizeof(trace)); +} +#else +static inline void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req) +{ +} +#endif + /* * function: zfcp_fsf_send_fcp_command_task_handler * @@ -3802,6 +3834,8 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) fcp_rsp_iu->fcp_sns_len); } + zfcp_fsf_trace_latency(fsf_req); + /* check FCP_RSP_INFO */ if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) { ZFCP_LOG_DEBUG("rsp_len is valid\n"); @@ -4358,10 +4392,14 @@ zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter, int req_flags, ZFCP_SBAL_TIMEOUT); if (ret < 0) return ret; - if (!ret) + if (!ret) { + atomic_inc(&adapter->qdio_outb_full); return -EIO; - } else if (!zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1)) + } + } else if (!zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1)) { + atomic_inc(&adapter->qdio_outb_full); return -EIO; + } return 0; } @@ -4530,6 +4568,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req) req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap if needed */ new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req); + fsf_req->qdio_outb_usage = atomic_read(&req_queue->free_count); fsf_req->issued = get_clock(); retval = do_QDIO(adapter->ccw_device, diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 7118661..230e807 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -213,6 +213,7 @@ #define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010 #define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 #define FSF_FEATURE_UPDATE_ALERT 0x00000100 +#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200 /* host connection features */ #define FSF_FEATURE_NPIV_MODE 0x00000001 @@ -322,11 +323,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 { @@ -427,7 +435,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]; @@ -469,7 +479,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 { @@ -486,4 +499,16 @@ struct fsf_qtcb { u8 log[FSF_QTCB_LOG_SIZE]; } __attribute__ ((packed)); +struct zfcp_blk_drv_data { +#define ZFCP_BLK_DRV_DATA_MAGIC 0x1 + u32 magic; +#define ZFCP_BLK_LAT_VALID 0x1 +#define ZFCP_BLK_REQ_ERROR 0x2 + u16 flags; + u8 inb_usage; + u8 outb_usage; + u64 channel_lat; + u64 fabric_lat; +} __attribute__ ((packed)); + #endif /* FSF_H */ diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 3297684..aa954b3 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -232,6 +232,27 @@ zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, unsigned int status, return retval; } +/* this needs to be called prior to updating the queue fill level */ +static void zfcp_qdio_account(struct zfcp_adapter *adapter) +{ + ktime_t now, _span; + struct timespec _now; + u64 span; + int free, used; + + spin_lock(&adapter->qdio_stat_lock); + ktime_get_ts(&_now); + now = timespec_to_ktime(_now); + _span = ktime_sub(now, adapter->req_q_time); + span = (u64)ktime_to_ns(_span); + do_div(span, 1000); + free = atomic_read(&adapter->request_queue.free_count); + used = QDIO_MAX_BUFFERS_PER_Q - free; + adapter->req_q_util += used * span; + adapter->req_q_time = now; + spin_unlock(&adapter->qdio_stat_lock); +} + /* * function: zfcp_qdio_request_handler * @@ -271,6 +292,8 @@ zfcp_qdio_request_handler(struct ccw_device *ccw_device, /* cleanup all SBALs being program-owned now */ zfcp_qdio_zero_sbals(queue->buffer, first_element, elements_processed); + zfcp_qdio_account(adapter); + /* increase free space in outbound queue */ atomic_add(elements_processed, &queue->free_count); ZFCP_LOG_DEBUG("free_count=%d\n", atomic_read(&queue->free_count)); @@ -302,6 +325,8 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, atomic_dec(&adapter->reqs_active); spin_unlock_irqrestore(&adapter->req_list_lock, flags); + fsf_req->qdio_inb_usage = atomic_read( + &adapter->response_queue.free_count); /* finish the FSF request */ zfcp_fsf_req_complete(fsf_req); } @@ -611,6 +636,7 @@ zfcp_qdio_sbals_from_segment(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, addr += length, remaining -= length) { /* get next free SBALE for new piece */ if (NULL == zfcp_qdio_sbale_next(fsf_req, sbtype)) { + atomic_inc(&fsf_req->adapter->qdio_outb_full); /* no SBALE left, clean up and leave */ zfcp_qdio_sbals_wipe(fsf_req); return -EINVAL; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 050f7c8..8e38afe 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -61,6 +61,7 @@ struct zfcp_data zfcp_data = { .use_clustering = 1, .sdev_attrs = zfcp_sysfs_sdev_attrs, .max_sectors = ZFCP_MAX_SECTORS, + .shost_attrs = zfcp_sysfs_shost_attrs, }, .driver_version = ZFCP_VERSION, }; diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c index e0bbcc4..fd3ce23 100644 --- a/drivers/s390/scsi/zfcp_sysfs_adapter.c +++ b/drivers/s390/scsi/zfcp_sysfs_adapter.c @@ -242,6 +242,50 @@ static struct attribute_group zfcp_adapter_attr_group = { .attrs = zfcp_adapter_attrs, }; +static ssize_t zfcp_sysfs_adapter_util_show(struct class_device *dev, char *buf) +{ + struct Scsi_Host *scsi_host = class_to_shost(dev); + struct fsf_qtcb_bottom_port *qtcb_port; + struct zfcp_adapter *adapter; + int retval; + + adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; + if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)) + return -EOPNOTSUPP; + + qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL); + if (!qtcb_port) + return -ENOMEM; + + retval = zfcp_fsf_exchange_port_data(NULL, 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 CLASS_DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, + NULL); + +static ssize_t zfcp_sysfs_adapter_q_full_show(struct class_device *dev, + char *buf) +{ + struct Scsi_Host *scsi_host = class_to_shost(dev); + struct zfcp_adapter *adapter = + (struct zfcp_adapter *) scsi_host->hostdata[0]; + + return sprintf(buf, "%d %llu\n", atomic_read(&adapter->qdio_outb_full), + (unsigned long long)adapter->req_q_util); +} +static CLASS_DEVICE_ATTR(queue_full, S_IRUGO, zfcp_sysfs_adapter_q_full_show, + NULL); + +struct class_device_attribute *zfcp_sysfs_shost_attrs[] = { + &class_device_attr_utilization, + &class_device_attr_queue_full, + NULL +}; + /** * zfcp_sysfs_create_adapter_files - create sysfs adapter files * @dev: pointer to belonging device diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 7520cc1..e1e0196 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -20,6 +20,7 @@ enum blktrace_cat { BLK_TC_PC = 1 << 9, /* pc requests */ BLK_TC_NOTIFY = 1 << 10, /* special message */ BLK_TC_AHEAD = 1 << 11, /* readahead */ + BLK_TC_DRV_DATA = 1 << 14, /* binary per-driver data */ BLK_TC_END = 1 << 15, /* only 16-bits, reminder */ }; @@ -46,6 +47,7 @@ enum blktrace_act { __BLK_TA_SPLIT, /* bio was split */ __BLK_TA_BOUNCE, /* bio was bounced */ __BLK_TA_REMAP, /* bio was remapped */ + __BLK_TA_DRV_DATA, /* driver-specific binary data */ }; /* @@ -66,6 +68,7 @@ enum blktrace_act { #define BLK_TA_SPLIT (__BLK_TA_SPLIT) #define BLK_TA_BOUNCE (__BLK_TA_BOUNCE) #define BLK_TA_REMAP (__BLK_TA_REMAP | BLK_TC_ACT(BLK_TC_QUEUE)) +#define BLK_TA_DRV_DATA (__BLK_TA_DRV_DATA | BLK_TC_ACT(BLK_TC_DRV_DATA)) #define BLK_IO_TRACE_MAGIC 0x65617400 #define BLK_IO_TRACE_VERSION 0x07 @@ -264,6 +267,34 @@ static inline void blk_add_trace_remap(struct request_queue *q, struct bio *bio, __blk_add_trace(bt, from, bio->bi_size, bio->bi_rw, BLK_TA_REMAP, !bio_flagged(bio, BIO_UPTODATE), sizeof(r), &r); } +/** + * blk_add_driver_data - Add binary message with driver-specific data + * @q: queue the io is for + * @rq: io request + * @data: driver-specific data + * @len: length of driver-specific data + * + * Description: + * Some drivers might want to write driver-specific data per request. + * + **/ +static inline void blk_add_driver_data(struct request_queue *q, + struct request *rq, + void *data, size_t len) +{ + struct blk_trace *bt = q->blk_trace; + + if (likely(!bt)) + return; + + if (blk_pc_request(rq)) + __blk_add_trace(bt, 0, rq->data_len, 0, BLK_TA_DRV_DATA, + rq->errors, len, data); + else + __blk_add_trace(bt, rq->hard_sector, rq->hard_nr_sectors << 9, + 0, BLK_TA_DRV_DATA, rq->errors, len, data); +} + #else /* !CONFIG_BLK_DEV_IO_TRACE */ #define blk_trace_ioctl(bdev, cmd, arg) (-ENOTTY) #define blk_trace_shutdown(q) do { } while (0) @@ -272,6 +303,7 @@ static inline void blk_add_trace_remap(struct request_queue *q, struct bio *bio, #define blk_add_trace_generic(q, rq, rw, what) do { } while (0) #define blk_add_trace_pdu_int(q, what, bio, pdu) do { } while (0) #define blk_add_trace_remap(q, bio, dev, f, t) do {} while (0) +#define blk_add_driver_data(q, rq, data, len) do {} while (0) #endif /* CONFIG_BLK_DEV_IO_TRACE */ #endif