diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2017-01-20 13:48:24 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-01-20 13:48:24 -0800 |
| commit | 7280bd073ede90c60ac0e6aba8a8af85cba23fe9 (patch) | |
| tree | 00c5a30dfae989773bd4f1f2a3b40a3da943a881 | |
| parent | e40f634b02b63aa168739ec51bd2a774e9adaa62 (diff) | |
| parent | b88a7af704cad5ab5f2973cc92c1056ceb155d6e (diff) | |
Merge "mmc: host: Add Ring buffer logging for MMC."
| -rw-r--r-- | drivers/mmc/core/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/mmc/core/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mmc/core/host.c | 3 | ||||
| -rw-r--r-- | drivers/mmc/core/ring_buffer.c | 123 | ||||
| -rw-r--r-- | include/linux/mmc/host.h | 2 | ||||
| -rw-r--r-- | include/linux/mmc/ring_buffer.h | 55 |
6 files changed, 195 insertions, 0 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 57cc6b29b2d0..9e0ccdc44d6b 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -2,6 +2,17 @@ # MMC core configuration # +config MMC_RING_BUFFER + bool "MMC_RING_BUFFER" + depends on MMC + default n + help + This enables the ring buffer tracing of significant + events for mmc driver to provide command history for + debugging purpose. + + If unsure, say N. + config MMC_EMBEDDED_SDIO boolean "MMC embedded SDIO device support (EXPERIMENTAL)" help diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 2c25138f28b7..60781dd192ab 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -10,3 +10,4 @@ mmc_core-y := core.o bus.o host.o \ quirks.o slot-gpio.o mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o +obj-$(CONFIG_MMC_RING_BUFFER) += ring_buffer.o diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index f6a54a8e1076..333f691a73c7 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -26,6 +26,8 @@ #include <linux/mmc/host.h> #include <linux/mmc/card.h> +#include <linux/mmc/ring_buffer.h> + #include <linux/mmc/slot-gpio.h> #include "core.h" @@ -869,6 +871,7 @@ int mmc_add_host(struct mmc_host *host) mmc_add_host_debugfs(host); #endif mmc_host_clk_sysfs_init(host); + mmc_trace_init(host); err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp); if (err) diff --git a/drivers/mmc/core/ring_buffer.c b/drivers/mmc/core/ring_buffer.c new file mode 100644 index 000000000000..83945e1cae40 --- /dev/null +++ b/drivers/mmc/core/ring_buffer.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include <linux/mmc/ring_buffer.h> +#include <linux/mmc/host.h> + +void mmc_stop_tracing(struct mmc_host *mmc) +{ + mmc->trace_buf.stop_tracing = true; +} + +void mmc_trace_write(struct mmc_host *mmc, + const char *fmt, ...) +{ + unsigned int idx; + va_list args; + char *event; + unsigned long flags; + char str[MMC_TRACE_EVENT_SZ]; + + if (unlikely(!mmc->trace_buf.data) || + unlikely(mmc->trace_buf.stop_tracing)) + return; + + /* + * Here an increment and modulus is used to keep + * index within array bounds. The cast to unsigned is + * necessary so increment and rolover wraps to 0 correctly + */ + spin_lock_irqsave(&mmc->trace_buf.trace_lock, flags); + mmc->trace_buf.wr_idx += 1; + idx = ((unsigned int)mmc->trace_buf.wr_idx) & + (MMC_TRACE_RBUF_NUM_EVENTS - 1); + spin_unlock_irqrestore(&mmc->trace_buf.trace_lock, flags); + + /* Catch some unlikely machine specific wrap-around bug */ + if (unlikely(idx > (MMC_TRACE_RBUF_NUM_EVENTS - 1))) { + pr_err("%s: %s: Invalid idx:%d for mmc trace, tracing stopped !\n", + mmc_hostname(mmc), __func__, idx); + mmc_stop_tracing(mmc); + return; + } + + event = &mmc->trace_buf.data[idx * MMC_TRACE_EVENT_SZ]; + va_start(args, fmt); + snprintf(str, MMC_TRACE_EVENT_SZ, "<%d> %lld: %s: %s", + raw_smp_processor_id(), + ktime_to_ns(ktime_get()), + mmc_hostname(mmc), fmt); + memset(event, '\0', MMC_TRACE_EVENT_SZ); + vscnprintf(event, MMC_TRACE_EVENT_SZ, str, args); + va_end(args); +} + +void mmc_trace_init(struct mmc_host *mmc) +{ + BUILD_BUG_ON_NOT_POWER_OF_2(MMC_TRACE_RBUF_NUM_EVENTS); + + mmc->trace_buf.data = (char *) + __get_free_pages(GFP_KERNEL|__GFP_ZERO, + MMC_TRACE_RBUF_SZ_ORDER); + + if (!mmc->trace_buf.data) { + pr_err("%s: %s: Unable to allocate trace for mmc\n", + __func__, mmc_hostname(mmc)); + return; + } + + spin_lock_init(&mmc->trace_buf.trace_lock); + mmc->trace_buf.wr_idx = -1; +} + +void mmc_trace_free(struct mmc_host *mmc) +{ + if (mmc->trace_buf.data) + free_pages((unsigned long)mmc->trace_buf.data, + MMC_TRACE_RBUF_SZ_ORDER); +} + +void mmc_dump_trace_buffer(struct mmc_host *mmc, struct seq_file *s) +{ + unsigned int idx, cur_idx; + unsigned int N = MMC_TRACE_RBUF_NUM_EVENTS - 1; + char *event; + unsigned long flags; + + if (!mmc->trace_buf.data) + return; + + spin_lock_irqsave(&mmc->trace_buf.trace_lock, flags); + idx = ((unsigned int)mmc->trace_buf.wr_idx) & N; + cur_idx = (idx + 1) & N; + + do { + event = &mmc->trace_buf.data[cur_idx * MMC_TRACE_EVENT_SZ]; + if (s) + seq_printf(s, "%s", (char *)event); + else + pr_err("%s", (char *)event); + cur_idx = (cur_idx + 1) & N; + if (cur_idx == idx) { + event = + &mmc->trace_buf.data[cur_idx * MMC_TRACE_EVENT_SZ]; + if (s) + seq_printf(s, "latest_event: %s", + (char *)event); + else + pr_err("latest_event: %s", (char *)event); + break; + } + } while (1); + spin_unlock_irqrestore(&mmc->trace_buf.trace_lock, flags); +} diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 276dbf19805b..804d89a825fc 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -22,6 +22,7 @@ #include <linux/mmc/core.h> #include <linux/mmc/card.h> #include <linux/mmc/pm.h> +#include <linux/mmc/ring_buffer.h> #define MMC_AUTOSUSPEND_DELAY_MS 3000 @@ -571,6 +572,7 @@ struct mmc_host { } perf; bool perf_enable; #endif + struct mmc_trace_buffer trace_buf; enum dev_state dev_status; bool wakeup_on_idle; struct mmc_cmdq_context_info cmdq_ctx; diff --git a/include/linux/mmc/ring_buffer.h b/include/linux/mmc/ring_buffer.h new file mode 100644 index 000000000000..e6bf163ffcfe --- /dev/null +++ b/include/linux/mmc/ring_buffer.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef __MMC_RING_BUFFER__ +#define __MMC_RING_BUFFER__ + +#include <linux/mmc/card.h> +#include <linux/smp.h> + +#include "core.h" + +#define MMC_TRACE_RBUF_SZ_ORDER 2 /* 2^2 pages */ +#define MMC_TRACE_RBUF_SZ (PAGE_SIZE * (1 << MMC_TRACE_RBUF_SZ_ORDER)) +#define MMC_TRACE_EVENT_SZ 256 +#define MMC_TRACE_RBUF_NUM_EVENTS (MMC_TRACE_RBUF_SZ / MMC_TRACE_EVENT_SZ) + +struct mmc_host; +struct mmc_trace_buffer { + int wr_idx; + bool stop_tracing; + spinlock_t trace_lock; + char *data; +}; + +#ifdef CONFIG_MMC_RING_BUFFER +void mmc_stop_tracing(struct mmc_host *mmc); +void mmc_trace_write(struct mmc_host *mmc, const char *fmt, ...); +void mmc_trace_init(struct mmc_host *mmc); +void mmc_trace_free(struct mmc_host *mmc); +void mmc_dump_trace_buffer(struct mmc_host *mmc, struct seq_file *s); +#else +static inline void mmc_stop_tracing(struct mmc_host *mmc) {} +static inline void mmc_trace_write(struct mmc_host *mmc, + const char *fmt, ...) {} +static inline void mmc_trace_init(struct mmc_host *mmc) {} +static inline void mmc_trace_free(struct mmc_host *mmc) {} +static inline void mmc_dump_trace_buffer(struct mmc_host *mmc, + struct seq_file *s) {} +#endif + +#define MMC_TRACE(mmc, fmt, ...) \ + mmc_trace_write(mmc, fmt, ##__VA_ARGS__) + +#endif /* __MMC_RING_BUFFER__ */ |
