All of lore.kernel.org
 help / color / mirror / Atom feed
From: dragos.tatulea@intel.com
To: linux-kernel@vger.kernel.org, linux-mmc@vger.kernel.org, cjb@laptop.org
Cc: kirill.shutemov@linux.intel.com, irina.tirdea@intel.com,
	octavian.purdila@intel.com, tony.luck@intel.com,
	keescook@chromium.org, dragos.tatulea@gmail.com,
	Adrian Hunter <adrian.hunter@intel.com>
Subject: [PATCH v2 06/26] mmc: block: add panic write support
Date: Thu,  8 Nov 2012 15:06:04 +0200	[thread overview]
Message-ID: <1352379984-18381-7-git-send-email-dragos.tatulea@intel.com> (raw)
In-Reply-To: <1352379984-18381-1-git-send-email-dragos.tatulea@intel.com>

From: Adrian Hunter <adrian.hunter@intel.com>

Define panic_write_operations for mmc block devices.
Also define host mmc panic ops for controling panic dumping.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/mmc/card/Kconfig |   11 ++
 drivers/mmc/card/block.c |  257 +++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/mmc/host.h |   92 +++++++++++++++++
 3 files changed, 359 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 3b1f783..06c09a3 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -50,6 +50,17 @@ config MMC_BLOCK_BOUNCE
 
 	  If unsure, say Y here.
 
+config MMC_BLOCK_PANIC_WRITE
+	bool "Panic write support"
+	depends on MMC_BLOCK
+	select BLK_DEV_PANIC_WRITE
+	default n
+	help
+	  Say Y here to support panic write. Panic write enables upper
+	  layers to write to MMC/SD cards during an oops or panic.
+	  However a host controller driver that supports panic writes
+	  is also needed. Panic writes are defined by mmc_panic_ops.
+
 config SDIO_UART
 	tristate "SDIO UART/GPS class support"
 	help
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 172a768..e18ce4a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -108,6 +108,11 @@ struct mmc_blk_data {
 	struct device_attribute force_ro;
 	struct device_attribute power_ro_lock;
 	int	area_type;
+#ifdef CONFIG_MMC_BLOCK_PANIC_WRITE
+	int			panic;
+	struct scatterlist	*panic_sg;
+	struct mmc_blk_request	*panic_brq;
+#endif
 };
 
 static DEFINE_MUTEX(open_lock);
@@ -517,7 +522,7 @@ static const struct block_device_operations mmc_bdops = {
 #endif
 };
 
-static inline int mmc_blk_part_switch(struct mmc_card *card,
+static int mmc_blk_part_switch(struct mmc_card *card,
 				      struct mmc_blk_data *md)
 {
 	int ret;
@@ -1432,6 +1437,253 @@ out:
 	return ret;
 }
 
+#ifdef CONFIG_MMC_BLOCK_PANIC_WRITE
+
+static int mmc_blk_panic_init_cleanup(struct block_device *bdev, int init)
+{
+	struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
+	struct mmc_card *card;
+	int err = 0;
+
+	if (!md)
+		return -ENODEV;
+
+	card = md->queue.card;
+	if (!card) {
+		err = -ENODEV;
+		goto out_put;
+	}
+
+	mmc_claim_host(card->host);
+
+	if (!init) {
+		mmc_panic_cleanup_host(card->host);
+		goto out_free_brq;
+	}
+
+	md->panic_sg = kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
+	if (!md->panic_sg) {
+		err = -ENOMEM;
+		goto out_release;
+	}
+	sg_init_table(md->panic_sg, 1);
+
+	md->panic_brq = kmalloc(sizeof(struct mmc_blk_request), GFP_KERNEL);
+	if (!md->panic_brq) {
+		err = -ENOMEM;
+		goto out_free_sg;
+	}
+
+	err = mmc_panic_init_host(card->host);
+	if (err)
+		goto out_free_brq;
+
+	goto out_release;
+
+out_free_brq:
+	kfree(md->panic_brq);
+out_free_sg:
+	kfree(md->panic_sg);
+out_release:
+	mmc_release_host(card->host);
+out_put:
+	mmc_blk_put(md);
+	return err;
+}
+
+static int mmc_blk_panic_init(struct block_device *bdev)
+{
+	return mmc_blk_panic_init_cleanup(bdev, 1);
+}
+
+static void mmc_blk_panic_cleanup(struct block_device *bdev)
+{
+	mmc_blk_panic_init_cleanup(bdev, 0);
+}
+
+static int get_card_status(struct mmc_card *card, u32 *status, int retries);
+
+static int mmc_blk_panic_do_write(struct mmc_blk_data *md,
+				  struct mmc_card *card, sector_t sect,
+				  void *addr, unsigned long len)
+{
+	struct mmc_blk_request *brq = md->panic_brq;
+	unsigned int blocks = len >> 9;
+	unsigned int blksz = 512;
+
+	int err = 0;
+
+	sg_init_one(md->panic_sg, addr, len);
+
+	memset(brq, 0, sizeof(struct mmc_blk_request));
+
+	brq->mrq.cmd = &brq->cmd;
+	brq->mrq.data = &brq->data;
+	brq->mrq.stop = &brq->stop;
+
+	if (blocks > 1)
+		brq->mrq.cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
+	else
+		brq->mrq.cmd->opcode = MMC_WRITE_BLOCK;
+
+	brq->mrq.cmd->arg = sect;
+	if (!mmc_card_blockaddr(card))
+		brq->mrq.cmd->arg <<= 9;
+
+	brq->mrq.cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	if (blocks == 1)
+		brq->mrq.stop = NULL;
+	else {
+		brq->mrq.stop->opcode = MMC_STOP_TRANSMISSION;
+		brq->mrq.stop->arg = 0;
+		brq->mrq.stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+	}
+
+	brq->mrq.data->blksz = blksz;
+	brq->mrq.data->blocks = blocks;
+	brq->mrq.data->flags = MMC_DATA_WRITE;
+	brq->mrq.data->sg = md->panic_sg;
+	brq->mrq.data->sg_len = 1;
+
+	mmc_set_data_timeout(brq->mrq.data, card);
+
+	mmc_wait_for_req(card->host, &brq->mrq);
+
+	if (brq->cmd.error)
+		err = brq->cmd.error;
+	else if (brq->stop.error)
+		err = brq->stop.error;
+	else if (brq->data.error)
+		err = brq->data.error;
+
+	if (!mmc_host_is_spi(card->host)) {
+		u32 status;
+
+		do {
+			err = get_card_status(card, &status, 5);
+			if (err)
+				break;
+		} while (!(status & R1_READY_FOR_DATA) ||
+			 (R1_CURRENT_STATE(status) == R1_STATE_PRG));
+	}
+
+	return err;
+}
+
+static int mmc_blk_panic_reset(struct mmc_blk_data *md, struct mmc_host *host)
+{
+	struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
+	int err;
+
+	err = mmc_blk_reset(md, host, 0);
+	if (err != -EOPNOTSUPP)
+		goto out;
+
+	/* No hardware reset so try a software reset */
+	mmc_power_save_host(host);
+	mmc_power_restore_host(host);
+out:
+	/* Partition may have changed, force a switch */
+	main_md->part_curr = -1;
+
+	return err;
+}
+
+/*
+ * Tuning while panicing is not supported, so disable speeds that require it
+ * and reset.
+ */
+static int mmc_blk_panic_no_tuning(struct mmc_blk_data *md,
+				   struct mmc_host *host)
+{
+	if (host->ocr & SD_OCR_S18R) {
+		host->caps &= ~(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+				MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+				MMC_CAP_UHS_DDR50);
+		host->ocr &= ~SD_OCR_S18R;
+		return mmc_blk_panic_reset(md, host);
+	}
+	return 0;
+}
+
+static int mmc_blk_panic_write(struct block_device *bdev, sector_t sect,
+			       void *addr, unsigned long len)
+{
+	struct mmc_blk_data *md = bdev->bd_disk->private_data;
+	struct mmc_card *card;
+	size_t n;
+	int err = 0, err2;
+
+	if (!md)
+		return -ENODEV;
+	card = md->queue.card;
+	if (!card)
+		return -ENODEV;
+
+	card->host->panic_task = current;
+
+	if (!md->panic) {
+		u32 status;
+
+		md->panic = 1;
+		mmc_panic_begin_host(card->host);
+		mmc_blk_panic_no_tuning(md, card->host);
+		err2 = get_card_status(card, &status, 0);
+		if (err2 || !(status & R1_READY_FOR_DATA) ||
+		    R1_CURRENT_STATE(status) != R1_STATE_TRAN)
+			mmc_blk_panic_reset(md, card->host);
+		mmc_set_blocklen(card, 512);
+	}
+
+	err = mmc_blk_part_switch(card, md);
+	if (err)
+		return err;
+
+	while (len) {
+		n = min_t(size_t, card->host->panic_max_size, len);
+		err2 = mmc_blk_panic_do_write(md, card, sect, addr, n);
+		if (err2 && !err)
+			err = err2;
+		len -= n;
+		addr += n;
+		sect += n >> 9;
+	}
+
+	return err;
+}
+
+static int mmc_blk_panic_flush(struct block_device *bdev)
+{
+	struct mmc_blk_data *md = bdev->bd_disk->private_data;
+	struct mmc_card *card;
+
+	if (!md)
+		return -ENODEV;
+	card = md->queue.card;
+	if (!card)
+		return -ENODEV;
+
+	if (md->panic) {
+		card->host->panic_task = current;
+		mmc_panic_end_host(card->host);
+		md->panic = 0;
+	}
+
+	card->host->panic_task = NULL;
+
+	return 0;
+}
+
+static const struct panic_write_operations mmc_pwops = {
+	.init			= mmc_blk_panic_init,
+	.cleanup		= mmc_blk_panic_cleanup,
+	.write			= mmc_blk_panic_write,
+	.flush			= mmc_blk_panic_flush,
+};
+
+#endif
+
 static inline int mmc_blk_readonly(struct mmc_card *card)
 {
 	return mmc_card_readonly(card) ||
@@ -1500,6 +1752,9 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
 	md->disk->major	= MMC_BLOCK_MAJOR;
 	md->disk->first_minor = devidx * perdev_minors;
 	md->disk->fops = &mmc_bdops;
+#ifdef CONFIG_MMC_BLOCK_PANIC_WRITE
+	md->disk->pwops = &mmc_pwops;
+#endif
 	md->disk->private_data = md;
 	md->disk->queue = md->queue.queue;
 	md->disk->driverfs_dev = parent;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7abb0e1..4205d2d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -13,6 +13,7 @@
 #include <linux/leds.h>
 #include <linux/mutex.h>
 #include <linux/sched.h>
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/fault-inject.h>
 
@@ -138,6 +139,17 @@ struct mmc_host_ops {
 	void	(*hw_reset)(struct mmc_host *host);
 };
 
+#ifdef CONFIG_MMC_BLOCK_PANIC_WRITE
+
+struct mmc_panic_ops {
+	int (*init)(struct mmc_host *host);
+	void (*cleanup)(struct mmc_host *host);
+	void (*begin)(struct mmc_host *host);
+	void (*end)(struct mmc_host *host);
+};
+
+#endif
+
 struct mmc_card;
 struct device;
 
@@ -257,6 +269,7 @@ struct mmc_host {
 #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
 #define MMC_CAP2_CD_ACTIVE_HIGH	(1 << 10)	/* Card-detect signal active high */
 #define MMC_CAP2_RO_ACTIVE_HIGH	(1 << 11)	/* Write-protect signal active high */
+#define MMC_CAP_PANIC_WRITE	(1 << 12)	/* Panic write support */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
@@ -337,6 +350,12 @@ struct mmc_host {
 
 	unsigned int		actual_clock;	/* Actual HC clock rate */
 
+#ifdef CONFIG_MMC_BLOCK_PANIC_WRITE
+	struct task_struct	*panic_task;	/* task that is panic writing */
+	const struct mmc_panic_ops *pops;	/* panic operations */
+	size_t			panic_max_size;	/* max data transfer size */
+#endif
+
 	unsigned long		private[0] ____cacheline_aligned;
 };
 
@@ -452,4 +471,77 @@ static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
 	return host->ios.clock;
 }
 #endif
+
+#ifdef CONFIG_MMC_BLOCK_PANIC_WRITE
+
+static inline int mmc_panic_task_active(struct mmc_host *host)
+{
+	return host->panic_task != NULL;
+}
+
+static inline int mmc_am_panic_task(struct mmc_host *host)
+{
+	return host->panic_task == current;
+}
+
+static inline int mmc_am_nonpanic_task(struct mmc_host *host)
+{
+	return host->panic_task && host->panic_task != current;
+}
+
+static inline void mmc_trap_nonpanic_tasks(struct mmc_host *host)
+{
+	while (host->panic_task && host->panic_task != current)
+		msleep(100);
+}
+
+static inline int mmc_panic_init_host(struct mmc_host *host)
+{
+	if (!(host->caps2 & MMC_CAP_PANIC_WRITE))
+		return -EOPNOTSUPP;
+	if (host->pops->init)
+		return host->pops->init(host);
+	return 0;
+}
+
+static inline void mmc_panic_cleanup_host(struct mmc_host *host)
+{
+	if (host->pops->cleanup)
+		host->pops->cleanup(host);
+}
+
+static inline void mmc_panic_begin_host(struct mmc_host *host)
+{
+if (host->pops->begin)
+		host->pops->begin(host);
+}
+
+static inline void mmc_panic_end_host(struct mmc_host *host)
+{
+	if (host->pops->end)
+		host->pops->end(host);
+}
+
+#else
+
+static inline int mmc_panic_task_active(struct mmc_host *host)
+{
+	return 0;
+}
+
+static inline int mmc_am_panic_task(struct mmc_host *host)
+{
+	return 0;
+}
+
+static inline int mmc_am_nonpanic_task(struct mmc_host *host)
+{
+	return 0;
+}
+
+static inline void mmc_trap_nonpanic_tasks(struct mmc_host *host)
+{
+}
+
+#endif
 #endif /* LINUX_MMC_HOST_H */
-- 
1.7.9.5


  parent reply	other threads:[~2012-11-08 13:10 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-11-08 13:05 [PATCH v2 00/26] pstore, mmc: add mmc as backend for pstore dragos.tatulea
2012-11-08 13:05 ` [PATCH v2 01/26] pstore: allow for big files dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 02/26] pstore: add flags dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 03/26] pstore: add flush dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 04/26] blkoops: add a block device oops / panic logger dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 05/26] block: add panic write dragos.tatulea
2012-11-08 13:06 ` dragos.tatulea [this message]
2012-11-08 13:06 ` [PATCH v2 07/26] mmc: panic write: bypass host claiming dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 08/26] mmc: panic write: bypass request completion dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 09/26] mmc: panic write: suppress host not claimed warnings dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 10/26] mmc: panic write: do not msleep dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 11/26] mmc: panic write: bypass clock gating dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 12/26] mmc: panic write: bypass regulators dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 13/26] mmc: panic write: trap non panic tasks dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 14/26] mmc: panic write: bypass bus ref locking dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 15/26] mmc: sdhci: panic write: bypass spin lock dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 16/26] mmc: sdhci: panic write: no sleeping dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 17/26] mmc: sdhci: panic write: call tasklets inline dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 18/26] mmc: sdhci: panic write: no timeout timer dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 19/26] mmc: sdhci: panic write: no runtime pm dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 20/26] mmc: sdhci: panic write: no tuning dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 21/26] mmc: sdhci: panic write: poll interrupts dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 22/26] mmc: sdhci: panic write: no dma mapping dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 23/26] mmc: sdhci: panic write: resume suspended host dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 24/26] mmc: sdhci: panic write: abort request in progress dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 25/26] mmc: sdhci: panic write: trap nonpanic tasks dragos.tatulea
2012-11-08 13:06 ` [PATCH v2 26/26] mmc: sdhci-pci: add panic write support dragos.tatulea

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1352379984-18381-7-git-send-email-dragos.tatulea@intel.com \
    --to=dragos.tatulea@intel.com \
    --cc=adrian.hunter@intel.com \
    --cc=cjb@laptop.org \
    --cc=dragos.tatulea@gmail.com \
    --cc=irina.tirdea@intel.com \
    --cc=keescook@chromium.org \
    --cc=kirill.shutemov@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=octavian.purdila@intel.com \
    --cc=tony.luck@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.