From patchwork Mon May 4 12:12:43 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sperl X-Patchwork-Id: 6325611 Return-Path: X-Original-To: patchwork-linux-spi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 701F4BEEE1 for ; Mon, 4 May 2015 12:13:02 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 681E120351 for ; Mon, 4 May 2015 12:13:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BFFC32037E for ; Mon, 4 May 2015 12:12:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752422AbbEDMM7 (ORCPT ); Mon, 4 May 2015 08:12:59 -0400 Received: from 212-186-180-163.dynamic.surfer.at ([212.186.180.163]:48467 "EHLO cgate.sperl.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752348AbbEDMM7 (ORCPT ); Mon, 4 May 2015 08:12:59 -0400 Received: from raspb.intern.sperl.org (account martin@sperl.org [10.10.10.32] verified) by sperl.org (CommuniGate Pro SMTP 6.1.2) with ESMTPSA id 6321318; Mon, 04 May 2015 12:12:56 +0000 From: kernel@martin.sperl.org To: Mark Brown , linux-spi@vger.kernel.org Cc: Martin Sperl Subject: [PATCH 2/2] spi: add statistics gathering and reporting methods Date: Mon, 4 May 2015 12:12:43 +0000 Message-Id: <1430741564-2849-2-git-send-email-kernel@martin.sperl.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1430741564-2849-1-git-send-email-kernel@martin.sperl.org> References: <1430741564-2849-1-git-send-email-kernel@martin.sperl.org> Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Martin Sperl the statistics are available: for each master via: /sys/class/spi_master/spi*/stat for each device via: /sys/class/spi_master/spi32766/spi32766.4/stat /sys/bus/spi/devices/spi32766.*/stat Example output: messages: 78 transfers: 78 errors: 0 timeout: 0 bytes: 122990 bytes-tx: 122990 bytes-rx: 122990 transfer-len-log2-histogram: 0 33 3 9 1 2 0 0 0 0 0 0 0 30 0 0 0 Note that log2-histogram is defined for bin n as: spi_transfer.len in the range: [ 2^n : 2^(n+1) [ with n=0 for spi_transfer.len = 0 bus-master drivers can include additional information via spi_master.show_stats Signed-off-by: Martin Sperl --- drivers/spi/spi.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 50910d8..615f60d 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -41,6 +41,114 @@ #define CREATE_TRACE_POINTS #include +/* helper macros to help updating statistics (locked and unlocked) */ +#define SPI_STATS_FIELD_ADD_NOLOCK(master, spi, key, value) \ + do { \ + master->stats.key += value; \ + spi->stats.key += value; \ + } while (0) + +#define SPI_STATS_FIELD_ADD(master, spi, key, value) \ + do { \ + unsigned long flags; \ + \ + spin_lock_irqsave(&master->stats_spinlock, flags); \ + SPI_STATS_FIELD_ADD_NOLOCK(master, spi, key, value); \ + spin_unlock_irqrestore(&master->stats_spinlock, flags); \ + } while (0) + +static ssize_t +spi_show_statistics(struct spi_statistics *stat, + char *buf, ssize_t buf_size) +{ + ssize_t len; + int i; + + len = snprintf(buf, + buf_size, + "messages: %ld\n" + "transfers: %ld\n" + "errors: %ld\n" + "timeout: %ld\n" + "bytes: %lld\n" + "bytes-tx: %lld\n" + "bytes-rx: %lld\n" + "transfer-len-log2-histogram:", + stat->messages, + stat->transfers, + stat->errors, + stat->timedout, + stat->bytes, + stat->bytes_tx, + stat->bytes_rx + ); + + for (i = 0; i < SPI_STATISTICS_L2HISTO_SIZE; i++) { + len += snprintf(buf + len, buf_size - len, + " %ld", stat->bytes_l2histo[i]); + } + + len += snprintf(buf + len, buf_size - len, "\n"); + + return len; +} + +static ssize_t +stat_show(struct device *dev, struct device_attribute *a, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + ssize_t len; + unsigned long flags; + + spin_lock_irqsave(&spi->master->stats_spinlock, flags); + + len = spi_show_statistics(&spi->stats, buf, PAGE_SIZE); + + /* call show_stats */ + if (spi->master->show_stats) + len += spi->master->show_stats(spi->master, spi, + buf + len, + PAGE_SIZE - len); + + spin_unlock_irqrestore(&spi->master->stats_spinlock, flags); + + return len; +} +static DEVICE_ATTR_RO(stat); + +static ssize_t +master_stat_show(struct device *dev, struct device_attribute *a, char *buf) +{ + struct spi_master *master = to_spi_master(dev); + ssize_t len; + unsigned long flags; + + spin_lock_irqsave(&master->stats_spinlock, flags); + + len = spi_show_statistics(&master->stats, buf, PAGE_SIZE); + + if (master->show_stats) + len += master->show_stats(master, NULL, + buf + len, + PAGE_SIZE - len); + + spin_unlock_irqrestore(&master->stats_spinlock, flags); + + return len; +} + +struct device_attribute dev_attr_master_stat = { + .attr = { .name = "stat", .mode = S_IRUGO }, + .show = master_stat_show, + .store = NULL +}; + +static struct attribute *spi_master_attrs[] = { + &dev_attr_master_stat.attr, + NULL, +}; +ATTRIBUTE_GROUPS(spi_master); + static void spidev_release(struct device *dev) { struct spi_device *spi = to_spi_device(dev); @@ -69,6 +177,7 @@ static DEVICE_ATTR_RO(modalias); static struct attribute *spi_dev_attrs[] = { &dev_attr_modalias.attr, + &dev_attr_stat.attr, NULL, }; ATTRIBUTE_GROUPS(spi_dev); @@ -679,12 +788,34 @@ static int spi_transfer_one_message(struct spi_master *master, bool keep_cs = false; int ret = 0; unsigned long ms = 1; + int l2size; + unsigned long flags; spi_set_cs(msg->spi, true); + SPI_STATS_FIELD_ADD(master, msg->spi, messages, 1); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { trace_spi_transfer_start(msg, xfer); + /* update statistics */ + l2size = (xfer->len) ? fls(xfer->len - 1) + 1 : 0; + l2size = min(l2size, SPI_STATISTICS_L2HISTO_SIZE - 1); + + spin_lock_irqsave(&master->stats_spinlock, flags); + SPI_STATS_FIELD_ADD_NOLOCK(master, msg->spi, transfers, 1); + SPI_STATS_FIELD_ADD_NOLOCK(master, msg->spi, + bytes, xfer->len); + if (xfer->tx_buf) + SPI_STATS_FIELD_ADD_NOLOCK(master, msg->spi, + bytes_tx, xfer->len); + if (xfer->rx_buf) + SPI_STATS_FIELD_ADD_NOLOCK(master, msg->spi, + bytes_rx, xfer->len); + SPI_STATS_FIELD_ADD_NOLOCK(master, msg->spi, + bytes_l2histo[l2size], 1); + spin_unlock_irqrestore(&master->stats_spinlock, flags); + if (xfer->tx_buf || xfer->rx_buf) { reinit_completion(&master->xfer_completion); @@ -692,6 +823,8 @@ static int spi_transfer_one_message(struct spi_master *master, if (ret < 0) { dev_err(&msg->spi->dev, "SPI transfer failed: %d\n", ret); + SPI_STATS_FIELD_ADD(master, msg->spi, + errors, 1); goto out; } @@ -707,6 +840,8 @@ static int spi_transfer_one_message(struct spi_master *master, if (ms == 0) { dev_err(&msg->spi->dev, "SPI transfer timed out\n"); + SPI_STATS_FIELD_ADD(master, msg->spi, + timedout, 1); msg->status = -ETIMEDOUT; } } else { @@ -1405,6 +1540,7 @@ static struct class spi_master_class = { .name = "spi_master", .owner = THIS_MODULE, .dev_release = spi_master_release, + .dev_groups = spi_master_groups, }; @@ -1928,6 +2064,8 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) message->spi = spi; + SPI_STATS_FIELD_ADD(master, spi, spi_async, 1); + trace_spi_message_submit(message); return master->transfer(spi, message); @@ -2064,6 +2202,8 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, message->context = &done; message->spi = spi; + SPI_STATS_FIELD_ADD(master, spi, spi_sync, 1); + if (!bus_locked) mutex_lock(&master->bus_lock_mutex); @@ -2091,8 +2231,11 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, /* Push out the messages in the calling context if we * can. */ - if (master->transfer == spi_queued_transfer) + if (master->transfer == spi_queued_transfer) { + SPI_STATS_FIELD_ADD(master, spi, + spi_sync_immediate, 1); __spi_pump_messages(master, false); + } wait_for_completion(&done); status = message->status;