* [PATCH V2] spi: expose spi_master and spi_device statistics via sysfs
@ 2015-06-22 13:00 kernel-TqfNSX0MhmxHKSADF0wUEw
[not found] ` <1434978036-2353-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
0 siblings, 1 reply; 2+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-06-22 13:00 UTC (permalink / raw)
To: Mark Brown, linux-spi-u79uwXL29TY76Z2rM5mHXA; +Cc: Martin Sperl
From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
per spi-master statistics accessible as:
/sys/class/spi_master/spi*/statistics/*
per spi-device statistics accessible via:
/sys/class/spi_master/spi*/spi*.*/statistics/*
The following statistics are exposed as separate "files" inside
these directories:
* messages number of spi_messages
* transfers number of spi_transfers
* bytes number of bytes transferred
* bytes_rx number of bytes transmitted
* bytes_tx number of bytes received
* errors number of errors encounterd
* timedout number of messages that have timed out
* spi_async number of spi_messages submitted using spi_async
* spi_sync number of spi_messages submitted using spi_sync
* spi_sync_immediate number of spi_messages submitted using spi_sync,
that are handled immediately without a context switch
to the spi_pump worker-thread
Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
drivers/spi/spi.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/spi/spi.h | 64 ++++++++++++++++++
2 files changed, 229 insertions(+), 3 deletions(-)
Changelog:
V1->V2: removal of transfer_histogram exporting multiple values in one file
- there is another patch that adds this in a one value per file
format
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index cf8b91b..07476ca 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -67,11 +67,141 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
}
static DEVICE_ATTR_RO(modalias);
+#define SPI_STATISTICS_ATTRS(field, file) \
+static ssize_t spi_master_##field##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct spi_master *master = container_of(dev, \
+ struct spi_master, dev); \
+ return spi_statistics_##field##_show(&master->statistics, buf); \
+} \
+static struct device_attribute dev_attr_spi_master_##field = { \
+ .attr = { .name = file, .mode = S_IRUGO }, \
+ .show = spi_master_##field##_show, \
+}; \
+static ssize_t spi_device_##field##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct spi_device *spi = container_of(dev, \
+ struct spi_device, dev); \
+ return spi_statistics_##field##_show(&spi->statistics, buf); \
+} \
+static struct device_attribute dev_attr_spi_device_##field = { \
+ .attr = { .name = file, .mode = S_IRUGO }, \
+ .show = spi_device_##field##_show, \
+}
+
+#define SPI_STATISTICS_SHOW_NAME(name, file, field, format_string) \
+static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \
+ char *buf) \
+{ \
+ unsigned long flags; \
+ ssize_t len; \
+ spin_lock_irqsave(&stat->lock, flags); \
+ len = sprintf(buf, format_string, stat->field); \
+ spin_unlock_irqrestore(&stat->lock, flags); \
+ return len; \
+} \
+SPI_STATISTICS_ATTRS(name, file)
+
+#define SPI_STATISTICS_SHOW(field, format_string) \
+ SPI_STATISTICS_SHOW_NAME(field, __stringify(field), \
+ field, format_string)
+
+SPI_STATISTICS_SHOW(messages, "%lu");
+SPI_STATISTICS_SHOW(transfers, "%lu");
+SPI_STATISTICS_SHOW(errors, "%lu");
+SPI_STATISTICS_SHOW(timedout, "%lu");
+
+SPI_STATISTICS_SHOW(spi_sync, "%lu");
+SPI_STATISTICS_SHOW(spi_sync_immediate, "%lu");
+SPI_STATISTICS_SHOW(spi_async, "%lu");
+
+SPI_STATISTICS_SHOW(bytes, "%llu");
+SPI_STATISTICS_SHOW(bytes_rx, "%llu");
+SPI_STATISTICS_SHOW(bytes_tx, "%llu");
+
static struct attribute *spi_dev_attrs[] = {
&dev_attr_modalias.attr,
NULL,
};
-ATTRIBUTE_GROUPS(spi_dev);
+
+static const struct attribute_group spi_dev_group = {
+ .attrs = spi_dev_attrs,
+};
+
+static struct attribute *spi_device_statistics_attrs[] = {
+ &dev_attr_spi_device_messages.attr,
+ &dev_attr_spi_device_transfers.attr,
+ &dev_attr_spi_device_errors.attr,
+ &dev_attr_spi_device_timedout.attr,
+ &dev_attr_spi_device_spi_sync.attr,
+ &dev_attr_spi_device_spi_sync_immediate.attr,
+ &dev_attr_spi_device_spi_async.attr,
+ &dev_attr_spi_device_bytes.attr,
+ &dev_attr_spi_device_bytes_rx.attr,
+ &dev_attr_spi_device_bytes_tx.attr,
+ NULL,
+};
+
+static const struct attribute_group spi_device_statistics_group = {
+ .name = "statistics",
+ .attrs = spi_device_statistics_attrs,
+};
+
+static const struct attribute_group *spi_dev_groups[] = {
+ &spi_dev_group,
+ &spi_device_statistics_group,
+ NULL,
+};
+
+static struct attribute *spi_master_statistics_attrs[] = {
+ &dev_attr_spi_master_messages.attr,
+ &dev_attr_spi_master_transfers.attr,
+ &dev_attr_spi_master_errors.attr,
+ &dev_attr_spi_master_timedout.attr,
+ &dev_attr_spi_master_spi_sync.attr,
+ &dev_attr_spi_master_spi_sync_immediate.attr,
+ &dev_attr_spi_master_spi_async.attr,
+ &dev_attr_spi_master_bytes.attr,
+ &dev_attr_spi_master_bytes_rx.attr,
+ &dev_attr_spi_master_bytes_tx.attr,
+ NULL,
+};
+
+static const struct attribute_group spi_master_statistics_group = {
+ .name = "statistics",
+ .attrs = spi_master_statistics_attrs,
+};
+
+static const struct attribute_group *spi_master_groups[] = {
+ &spi_master_statistics_group,
+ NULL,
+};
+
+void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
+ struct spi_transfer *xfer,
+ struct spi_master *master)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stats->lock, flags);
+
+ stats->transfers++;
+
+ stats->bytes += xfer->len;
+ if ((xfer->tx_buf) &&
+ (xfer->tx_buf != master->dummy_tx))
+ stats->bytes_tx += xfer->len;
+ if ((xfer->rx_buf) &&
+ (xfer->rx_buf != master->dummy_rx))
+ stats->bytes_rx += xfer->len;
+
+ spin_unlock_irqrestore(&stats->lock, flags);
+}
+EXPORT_SYMBOL_GPL(spi_statistics_add_transfer_stats);
/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
* and the sysfs version makes coldplug work too.
@@ -249,6 +379,9 @@ struct spi_device *spi_alloc_device(struct spi_master *master)
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
spi->cs_gpio = -ENOENT;
+
+ spin_lock_init(&spi->statistics.lock);
+
device_initialize(&spi->dev);
return spi;
}
@@ -689,17 +822,29 @@ static int spi_transfer_one_message(struct spi_master *master,
bool keep_cs = false;
int ret = 0;
unsigned long ms = 1;
+ struct spi_statistics *statm = &master->statistics;
+ struct spi_statistics *stats = &msg->spi->statistics;
spi_set_cs(msg->spi, true);
+ SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
+ SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
+
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
trace_spi_transfer_start(msg, xfer);
+ spi_statistics_add_transfer_stats(statm, xfer, master);
+ spi_statistics_add_transfer_stats(stats, xfer, master);
+
if (xfer->tx_buf || xfer->rx_buf) {
reinit_completion(&master->xfer_completion);
ret = master->transfer_one(master, msg->spi, xfer);
if (ret < 0) {
+ SPI_STATISTICS_INCREMENT_FIELD(statm,
+ errors);
+ SPI_STATISTICS_INCREMENT_FIELD(stats,
+ errors);
dev_err(&msg->spi->dev,
"SPI transfer failed: %d\n", ret);
goto out;
@@ -715,6 +860,10 @@ static int spi_transfer_one_message(struct spi_master *master,
}
if (ms == 0) {
+ SPI_STATISTICS_INCREMENT_FIELD(statm,
+ timedout);
+ SPI_STATISTICS_INCREMENT_FIELD(stats,
+ timedout);
dev_err(&msg->spi->dev,
"SPI transfer timed out\n");
msg->status = -ETIMEDOUT;
@@ -1416,10 +1565,10 @@ static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
+ .dev_groups = spi_master_groups,
};
-
/**
* spi_alloc_master - allocate SPI master controller
* @dev: the controller, possibly using the platform_bus
@@ -1585,6 +1734,8 @@ int spi_register_master(struct spi_master *master)
goto done;
}
}
+ /* add statistics */
+ spin_lock_init(&master->statistics.lock);
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);
@@ -1939,6 +2090,9 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
message->spi = spi;
+ SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
+ SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);
+
trace_spi_message_submit(message);
return master->transfer(spi, message);
@@ -2075,6 +2229,9 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message,
message->context = &done;
message->spi = spi;
+ SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
+ SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
+
if (!bus_locked)
mutex_lock(&master->bus_lock_mutex);
@@ -2102,8 +2259,13 @@ 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_STATISTICS_INCREMENT_FIELD(&master->statistics,
+ spi_sync_immediate);
+ SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
+ spi_sync_immediate);
__spi_pump_messages(master, false);
+ }
wait_for_completion(&done);
status = message->status;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index d673072..269e8af 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -23,6 +23,8 @@
#include <linux/scatterlist.h>
struct dma_chan;
+struct spi_master;
+struct spi_transfer;
/*
* INTERFACES between SPI master-side drivers and SPI infrastructure.
@@ -31,6 +33,59 @@ struct dma_chan;
extern struct bus_type spi_bus_type;
/**
+ * struct spi_statistics - statistics for spi transfers
+ * @clock: lock protecting this structure
+ *
+ * @messages: number of spi-messages handled
+ * @transfers: number of spi_transfers handled
+ * @errors: number of errors during spi_transfer
+ * @timedout: number of timeouts during spi_transfer
+ *
+ * @spi_sync: number of times spi_sync is used
+ * @spi_sync_immediate:
+ * number of times spi_sync is executed immediately
+ * in calling context without queuing and scheduling
+ * @spi_async: number of times spi_async is used
+ *
+ * @bytes: number of bytes transferred to/from device
+ * @bytes_tx: number of bytes sent to device
+ * @bytes_rx: number of bytes received from device
+ *
+ */
+struct spi_statistics {
+ spinlock_t lock; /* lock for the whole structure */
+
+ unsigned long messages;
+ unsigned long transfers;
+ unsigned long errors;
+ unsigned long timedout;
+
+ unsigned long spi_sync;
+ unsigned long spi_sync_immediate;
+ unsigned long spi_async;
+
+ unsigned long long bytes;
+ unsigned long long bytes_rx;
+ unsigned long long bytes_tx;
+
+};
+
+void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
+ struct spi_transfer *xfer,
+ struct spi_master *master);
+
+#define SPI_STATISTICS_ADD_TO_FIELD(stats, field, count) \
+ do { \
+ unsigned long flags; \
+ spin_lock_irqsave(&(stats)->lock, flags); \
+ (stats)->field += count; \
+ spin_unlock_irqrestore(&(stats)->lock, flags); \
+ } while (0)
+
+#define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \
+ SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1)
+
+/**
* struct spi_device - Master side proxy for an SPI slave device
* @dev: Driver model representation of the device.
* @master: SPI controller used with the device.
@@ -60,6 +115,8 @@ extern struct bus_type spi_bus_type;
* @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
* when not using a GPIO line)
*
+ * @statistics: statistics for the spi_device
+ *
* A @spi_device is used to interchange data between an SPI slave
* (usually a discrete chip) and CPU memory.
*
@@ -98,6 +155,9 @@ struct spi_device {
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
+ /* the statistics */
+ struct spi_statistics statistics;
+
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
@@ -296,6 +356,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself).
+ * @statistics: statistics for the spi_master
* @dma_tx: DMA transmit channel
* @dma_rx: DMA receive channel
* @dummy_rx: dummy receive buffer for full-duplex devices
@@ -452,6 +513,9 @@ struct spi_master {
/* gpio chip select */
int *cs_gpios;
+ /* statistics */
+ struct spi_statistics statistics;
+
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Applied "spi: expose spi_master and spi_device statistics via sysfs" to the spi tree
[not found] ` <1434978036-2353-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
@ 2015-07-07 13:58 ` Mark Brown
0 siblings, 0 replies; 2+ messages in thread
From: Mark Brown @ 2015-07-07 13:58 UTC (permalink / raw)
To: Martin Sperl, Mark Brown; +Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA
The patch
spi: expose spi_master and spi_device statistics via sysfs
has been applied to the spi tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From eca2ebc7e007c9e2b8f5ecfcfc74b53fbe68e42b Mon Sep 17 00:00:00 2001
From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
Date: Mon, 22 Jun 2015 13:00:36 +0000
Subject: [PATCH] spi: expose spi_master and spi_device statistics via sysfs
per spi-master statistics accessible as:
/sys/class/spi_master/spi*/statistics/*
per spi-device statistics accessible via:
/sys/class/spi_master/spi*/spi*.*/statistics/*
The following statistics are exposed as separate "files" inside
these directories:
* messages number of spi_messages
* transfers number of spi_transfers
* bytes number of bytes transferred
* bytes_rx number of bytes transmitted
* bytes_tx number of bytes received
* errors number of errors encounterd
* timedout number of messages that have timed out
* spi_async number of spi_messages submitted using spi_async
* spi_sync number of spi_messages submitted using spi_sync
* spi_sync_immediate number of spi_messages submitted using spi_sync,
that are handled immediately without a context switch
to the spi_pump worker-thread
Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
Signed-off-by: Mark Brown <broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
drivers/spi/spi.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/spi/spi.h | 64 ++++++++++++++++++
2 files changed, 229 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index cf8b91b..07476ca 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -67,11 +67,141 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
}
static DEVICE_ATTR_RO(modalias);
+#define SPI_STATISTICS_ATTRS(field, file) \
+static ssize_t spi_master_##field##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct spi_master *master = container_of(dev, \
+ struct spi_master, dev); \
+ return spi_statistics_##field##_show(&master->statistics, buf); \
+} \
+static struct device_attribute dev_attr_spi_master_##field = { \
+ .attr = { .name = file, .mode = S_IRUGO }, \
+ .show = spi_master_##field##_show, \
+}; \
+static ssize_t spi_device_##field##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct spi_device *spi = container_of(dev, \
+ struct spi_device, dev); \
+ return spi_statistics_##field##_show(&spi->statistics, buf); \
+} \
+static struct device_attribute dev_attr_spi_device_##field = { \
+ .attr = { .name = file, .mode = S_IRUGO }, \
+ .show = spi_device_##field##_show, \
+}
+
+#define SPI_STATISTICS_SHOW_NAME(name, file, field, format_string) \
+static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \
+ char *buf) \
+{ \
+ unsigned long flags; \
+ ssize_t len; \
+ spin_lock_irqsave(&stat->lock, flags); \
+ len = sprintf(buf, format_string, stat->field); \
+ spin_unlock_irqrestore(&stat->lock, flags); \
+ return len; \
+} \
+SPI_STATISTICS_ATTRS(name, file)
+
+#define SPI_STATISTICS_SHOW(field, format_string) \
+ SPI_STATISTICS_SHOW_NAME(field, __stringify(field), \
+ field, format_string)
+
+SPI_STATISTICS_SHOW(messages, "%lu");
+SPI_STATISTICS_SHOW(transfers, "%lu");
+SPI_STATISTICS_SHOW(errors, "%lu");
+SPI_STATISTICS_SHOW(timedout, "%lu");
+
+SPI_STATISTICS_SHOW(spi_sync, "%lu");
+SPI_STATISTICS_SHOW(spi_sync_immediate, "%lu");
+SPI_STATISTICS_SHOW(spi_async, "%lu");
+
+SPI_STATISTICS_SHOW(bytes, "%llu");
+SPI_STATISTICS_SHOW(bytes_rx, "%llu");
+SPI_STATISTICS_SHOW(bytes_tx, "%llu");
+
static struct attribute *spi_dev_attrs[] = {
&dev_attr_modalias.attr,
NULL,
};
-ATTRIBUTE_GROUPS(spi_dev);
+
+static const struct attribute_group spi_dev_group = {
+ .attrs = spi_dev_attrs,
+};
+
+static struct attribute *spi_device_statistics_attrs[] = {
+ &dev_attr_spi_device_messages.attr,
+ &dev_attr_spi_device_transfers.attr,
+ &dev_attr_spi_device_errors.attr,
+ &dev_attr_spi_device_timedout.attr,
+ &dev_attr_spi_device_spi_sync.attr,
+ &dev_attr_spi_device_spi_sync_immediate.attr,
+ &dev_attr_spi_device_spi_async.attr,
+ &dev_attr_spi_device_bytes.attr,
+ &dev_attr_spi_device_bytes_rx.attr,
+ &dev_attr_spi_device_bytes_tx.attr,
+ NULL,
+};
+
+static const struct attribute_group spi_device_statistics_group = {
+ .name = "statistics",
+ .attrs = spi_device_statistics_attrs,
+};
+
+static const struct attribute_group *spi_dev_groups[] = {
+ &spi_dev_group,
+ &spi_device_statistics_group,
+ NULL,
+};
+
+static struct attribute *spi_master_statistics_attrs[] = {
+ &dev_attr_spi_master_messages.attr,
+ &dev_attr_spi_master_transfers.attr,
+ &dev_attr_spi_master_errors.attr,
+ &dev_attr_spi_master_timedout.attr,
+ &dev_attr_spi_master_spi_sync.attr,
+ &dev_attr_spi_master_spi_sync_immediate.attr,
+ &dev_attr_spi_master_spi_async.attr,
+ &dev_attr_spi_master_bytes.attr,
+ &dev_attr_spi_master_bytes_rx.attr,
+ &dev_attr_spi_master_bytes_tx.attr,
+ NULL,
+};
+
+static const struct attribute_group spi_master_statistics_group = {
+ .name = "statistics",
+ .attrs = spi_master_statistics_attrs,
+};
+
+static const struct attribute_group *spi_master_groups[] = {
+ &spi_master_statistics_group,
+ NULL,
+};
+
+void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
+ struct spi_transfer *xfer,
+ struct spi_master *master)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stats->lock, flags);
+
+ stats->transfers++;
+
+ stats->bytes += xfer->len;
+ if ((xfer->tx_buf) &&
+ (xfer->tx_buf != master->dummy_tx))
+ stats->bytes_tx += xfer->len;
+ if ((xfer->rx_buf) &&
+ (xfer->rx_buf != master->dummy_rx))
+ stats->bytes_rx += xfer->len;
+
+ spin_unlock_irqrestore(&stats->lock, flags);
+}
+EXPORT_SYMBOL_GPL(spi_statistics_add_transfer_stats);
/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
* and the sysfs version makes coldplug work too.
@@ -249,6 +379,9 @@ struct spi_device *spi_alloc_device(struct spi_master *master)
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
spi->cs_gpio = -ENOENT;
+
+ spin_lock_init(&spi->statistics.lock);
+
device_initialize(&spi->dev);
return spi;
}
@@ -689,17 +822,29 @@ static int spi_transfer_one_message(struct spi_master *master,
bool keep_cs = false;
int ret = 0;
unsigned long ms = 1;
+ struct spi_statistics *statm = &master->statistics;
+ struct spi_statistics *stats = &msg->spi->statistics;
spi_set_cs(msg->spi, true);
+ SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
+ SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
+
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
trace_spi_transfer_start(msg, xfer);
+ spi_statistics_add_transfer_stats(statm, xfer, master);
+ spi_statistics_add_transfer_stats(stats, xfer, master);
+
if (xfer->tx_buf || xfer->rx_buf) {
reinit_completion(&master->xfer_completion);
ret = master->transfer_one(master, msg->spi, xfer);
if (ret < 0) {
+ SPI_STATISTICS_INCREMENT_FIELD(statm,
+ errors);
+ SPI_STATISTICS_INCREMENT_FIELD(stats,
+ errors);
dev_err(&msg->spi->dev,
"SPI transfer failed: %d\n", ret);
goto out;
@@ -715,6 +860,10 @@ static int spi_transfer_one_message(struct spi_master *master,
}
if (ms == 0) {
+ SPI_STATISTICS_INCREMENT_FIELD(statm,
+ timedout);
+ SPI_STATISTICS_INCREMENT_FIELD(stats,
+ timedout);
dev_err(&msg->spi->dev,
"SPI transfer timed out\n");
msg->status = -ETIMEDOUT;
@@ -1416,10 +1565,10 @@ static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
+ .dev_groups = spi_master_groups,
};
-
/**
* spi_alloc_master - allocate SPI master controller
* @dev: the controller, possibly using the platform_bus
@@ -1585,6 +1734,8 @@ int spi_register_master(struct spi_master *master)
goto done;
}
}
+ /* add statistics */
+ spin_lock_init(&master->statistics.lock);
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);
@@ -1939,6 +2090,9 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
message->spi = spi;
+ SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
+ SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);
+
trace_spi_message_submit(message);
return master->transfer(spi, message);
@@ -2075,6 +2229,9 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message,
message->context = &done;
message->spi = spi;
+ SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
+ SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
+
if (!bus_locked)
mutex_lock(&master->bus_lock_mutex);
@@ -2102,8 +2259,13 @@ 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_STATISTICS_INCREMENT_FIELD(&master->statistics,
+ spi_sync_immediate);
+ SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
+ spi_sync_immediate);
__spi_pump_messages(master, false);
+ }
wait_for_completion(&done);
status = message->status;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index d673072..269e8af 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -23,6 +23,8 @@
#include <linux/scatterlist.h>
struct dma_chan;
+struct spi_master;
+struct spi_transfer;
/*
* INTERFACES between SPI master-side drivers and SPI infrastructure.
@@ -31,6 +33,59 @@ struct dma_chan;
extern struct bus_type spi_bus_type;
/**
+ * struct spi_statistics - statistics for spi transfers
+ * @clock: lock protecting this structure
+ *
+ * @messages: number of spi-messages handled
+ * @transfers: number of spi_transfers handled
+ * @errors: number of errors during spi_transfer
+ * @timedout: number of timeouts during spi_transfer
+ *
+ * @spi_sync: number of times spi_sync is used
+ * @spi_sync_immediate:
+ * number of times spi_sync is executed immediately
+ * in calling context without queuing and scheduling
+ * @spi_async: number of times spi_async is used
+ *
+ * @bytes: number of bytes transferred to/from device
+ * @bytes_tx: number of bytes sent to device
+ * @bytes_rx: number of bytes received from device
+ *
+ */
+struct spi_statistics {
+ spinlock_t lock; /* lock for the whole structure */
+
+ unsigned long messages;
+ unsigned long transfers;
+ unsigned long errors;
+ unsigned long timedout;
+
+ unsigned long spi_sync;
+ unsigned long spi_sync_immediate;
+ unsigned long spi_async;
+
+ unsigned long long bytes;
+ unsigned long long bytes_rx;
+ unsigned long long bytes_tx;
+
+};
+
+void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
+ struct spi_transfer *xfer,
+ struct spi_master *master);
+
+#define SPI_STATISTICS_ADD_TO_FIELD(stats, field, count) \
+ do { \
+ unsigned long flags; \
+ spin_lock_irqsave(&(stats)->lock, flags); \
+ (stats)->field += count; \
+ spin_unlock_irqrestore(&(stats)->lock, flags); \
+ } while (0)
+
+#define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \
+ SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1)
+
+/**
* struct spi_device - Master side proxy for an SPI slave device
* @dev: Driver model representation of the device.
* @master: SPI controller used with the device.
@@ -60,6 +115,8 @@ extern struct bus_type spi_bus_type;
* @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
* when not using a GPIO line)
*
+ * @statistics: statistics for the spi_device
+ *
* A @spi_device is used to interchange data between an SPI slave
* (usually a discrete chip) and CPU memory.
*
@@ -98,6 +155,9 @@ struct spi_device {
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
+ /* the statistics */
+ struct spi_statistics statistics;
+
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
@@ -296,6 +356,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself).
+ * @statistics: statistics for the spi_master
* @dma_tx: DMA transmit channel
* @dma_rx: DMA receive channel
* @dummy_rx: dummy receive buffer for full-duplex devices
@@ -452,6 +513,9 @@ struct spi_master {
/* gpio chip select */
int *cs_gpios;
+ /* statistics */
+ struct spi_statistics statistics;
+
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2015-07-07 13:58 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-22 13:00 [PATCH V2] spi: expose spi_master and spi_device statistics via sysfs kernel-TqfNSX0MhmxHKSADF0wUEw
[not found] ` <1434978036-2353-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2015-07-07 13:58 ` Applied "spi: expose spi_master and spi_device statistics via sysfs" to the spi tree Mark Brown
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.