Hi Kees, Do you see any issues to ACK this if the mmc pstore registration is through register_pstore_blk()? As Uffe said, eMMC would need to go through psblk_generic_blk_read()/write() instead of mmcpstore_read()/write(). Thanks, Bhaskara >-----Original Message----- >From: Ulf Hansson <ulf.hansson@xxxxxxxxxx> >Sent: Wednesday, January 13, 2021 5:16 PM >To: Bhaskara Budiredla <bbudiredla@xxxxxxxxxxx> >Cc: Kees Cook <keescook@xxxxxxxxxxxx>; Colin Cross ><ccross@xxxxxxxxxxx>; Tony Luck <tony.luck@xxxxxxxxx>; Sunil Kovvuri >Goutham <sgoutham@xxxxxxxxxxx>; linux-mmc@xxxxxxxxxxxxxxx; Linux >Kernel Mailing List <linux-kernel@xxxxxxxxxxxxxxx> >Subject: [EXT] Re: [PATCH v4 1/2] mmc: Support kmsg dumper based on >pstore/blk > >External Email > >---------------------------------------------------------------------- >On Wed, 23 Dec 2020 at 15:41, Bhaskara Budiredla ><bbudiredla@xxxxxxxxxxx> wrote: >> >> This patch introduces to mmcpstore. The functioning of mmcpstore is >> similar to mtdpstore. mmcpstore works on FTL based flash devices >> whereas mtdpstore works on raw flash devices. When the system crashes, >> mmcpstore stores the kmsg panic and oops logs to a user specified MMC >> device. >> >> It collects the details about the host MMC device through pstore/blk >> "blkdev" parameter. The user can specify the MMC device in many ways >> by checking in Documentation/admin-guide/pstore-blk.rst. >> >> The individual mmc host drivers have to define suitable polling and >> cleanup subroutines to write kmsg panic/oops logs through mmcpstore. >> These new host operations are needed as pstore panic write runs with >> interrupts disabled. >> >> Signed-off-by: Bhaskara Budiredla <bbudiredla@xxxxxxxxxxx> > >Just to make it clear, to avoid confusions. > >I will not consider to pick this up, as long as the *non-panic* read/write >support is implemented through pstore specific callbacks. >Instead those requests should go through the regular block I/O path, as like >any other file system. > >Kind regards >Ulf Hansson > >> --- >> drivers/mmc/core/Kconfig | 14 +- >> drivers/mmc/core/Makefile | 1 + >> drivers/mmc/core/block.c | 19 +++ >> drivers/mmc/core/block.h | 9 + >> drivers/mmc/core/core.c | 44 +++++ >> drivers/mmc/core/mmcpstore.c | 310 >+++++++++++++++++++++++++++++++++++ >> include/linux/mmc/core.h | 5 + >> include/linux/mmc/host.h | 12 ++ >> 8 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 >> drivers/mmc/core/mmcpstore.c >> >> diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index >> c12fe13e4b14..4c651da4f2d2 100644 >> --- a/drivers/mmc/core/Kconfig >> +++ b/drivers/mmc/core/Kconfig >> @@ -34,9 +34,22 @@ config PWRSEQ_SIMPLE >> This driver can also be built as a module. If so, the module >> will be called pwrseq_simple. >> >> +config MMC_PSTORE_BACKEND >> + bool "Log panic/oops to a MMC buffer" >> + depends on MMC_BLOCK >> + help >> + This option will let you create platform backend to store kmsg >> + crash dumps to a user specified MMC device. This is primarily >> + based on pstore/blk. >> + >> +config MMC_PSTORE >> + tristate >> + select PSTORE_BLK >> + >> config MMC_BLOCK >> tristate "MMC block device driver" >> depends on BLOCK >> + select MMC_PSTORE if MMC_PSTORE_BACKEND=y >> default y >> help >> Say Y here to enable the MMC block device driver support. >> @@ -80,4 +93,3 @@ config MMC_TEST >> >> This driver is only of interest to those developing or >> testing a host driver. Most people should say N here. >> - >> diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile >> index 95ffe008ebdf..7cb9a3af4827 100644 >> --- a/drivers/mmc/core/Makefile >> +++ b/drivers/mmc/core/Makefile >> @@ -16,5 +16,6 @@ obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o >> mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o >> obj-$(CONFIG_MMC_BLOCK) += mmc_block.o >> mmc_block-objs := block.o queue.o >> +mmc_block-$(CONFIG_MMC_PSTORE) += mmcpstore.o >> obj-$(CONFIG_MMC_TEST) += mmc_test.o >> obj-$(CONFIG_SDIO_UART) += sdio_uart.o >> diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index >> 8d3df0be0355..ed012a91e3a3 100644 >> --- a/drivers/mmc/core/block.c >> +++ b/drivers/mmc/core/block.c >> @@ -2870,6 +2870,21 @@ static void mmc_blk_remove_debugfs(struct >> mmc_card *card, >> >> #endif /* CONFIG_DEBUG_FS */ >> >> +#if IS_ENABLED(CONFIG_MMC_PSTORE) >> +sector_t mmc_blk_get_part(struct mmc_card *card, int part_num, >> +sector_t *size) { >> + struct mmc_blk_data *md = dev_get_drvdata(&card->dev); >> + struct gendisk *disk = md->disk; >> + struct disk_part_tbl *part_tbl = disk->part_tbl; >> + >> + if (part_num < 0 || part_num >= part_tbl->len) >> + return 0; >> + >> + *size = part_tbl->part[part_num]->nr_sects << SECTOR_SHIFT; >> + return part_tbl->part[part_num]->start_sect; >> +} >> +#endif >> + >> static int mmc_blk_probe(struct mmc_card *card) { >> struct mmc_blk_data *md, *part_md; @@ -2913,6 +2928,9 @@ >> static int mmc_blk_probe(struct mmc_card *card) >> goto out; >> } >> >> + if (mmc_card_mmc(card) || mmc_card_sd(card)) >> + mmcpstore_card_set(card, md->disk->disk_name); >> + >> /* Add two debugfs entries */ >> mmc_blk_add_debugfs(card, md); >> >> @@ -3060,6 +3078,7 @@ static void __exit mmc_blk_exit(void) >> unregister_blkdev(MMC_BLOCK_MAJOR, "mmc"); >> unregister_chrdev_region(mmc_rpmb_devt, MAX_DEVICES); >> bus_unregister(&mmc_rpmb_bus_type); >> + unregister_mmcpstore(); >> } >> >> module_init(mmc_blk_init); >> diff --git a/drivers/mmc/core/block.h b/drivers/mmc/core/block.h index >> 31153f656f41..2a4ee5568194 100644 >> --- a/drivers/mmc/core/block.h >> +++ b/drivers/mmc/core/block.h >> @@ -16,5 +16,14 @@ void mmc_blk_mq_recovery(struct mmc_queue >*mq); >> struct work_struct; >> >> void mmc_blk_mq_complete_work(struct work_struct *work); >> +#if IS_ENABLED(CONFIG_MMC_PSTORE) >> +sector_t mmc_blk_get_part(struct mmc_card *card, int part_num, >> +sector_t *size); void mmcpstore_card_set(struct mmc_card *card, const >> +char *disk_name); void unregister_mmcpstore(void); #else static >> +inline void mmcpstore_card_set(struct mmc_card *card, >> + const char *disk_name) {} >> +static inline void unregister_mmcpstore(void) {} #endif >> >> #endif >> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index >> d42037f0f10d..32df03f4c848 100644 >> --- a/drivers/mmc/core/core.c >> +++ b/drivers/mmc/core/core.c >> @@ -569,6 +569,30 @@ int mmc_cqe_recovery(struct mmc_host *host) } >> EXPORT_SYMBOL(mmc_cqe_recovery); >> >> +#if IS_ENABLED(CONFIG_MMC_PSTORE) >> +/** >> + * mmc_wait_for_pstore_req - initiate a blocking mmc request >> + * @host: MMC host to start command >> + * @mrq: MMC request to start >> + * >> + * Start a blocking MMC request for a host and wait for the request >> + * to complete that is based on polling and timeout. >> + */ >> +void mmc_wait_for_pstore_req(struct mmc_host *host, struct >> +mmc_request *mrq) { >> + unsigned int timeout; >> + >> + host->ops->req_cleanup_pending(host); >> + mmc_start_request(host, mrq); >> + >> + if (mrq->data) { >> + timeout = mrq->data->timeout_ns / NSEC_PER_MSEC; >> + host->ops->req_completion_poll(host, timeout); >> + } >> +} >> +EXPORT_SYMBOL(mmc_wait_for_pstore_req); >> +#endif >> + >> /** >> * mmc_is_req_done - Determine if a 'cap_cmd_during_tfr' request is >done >> * @host: MMC host >> @@ -817,6 +841,26 @@ int __mmc_claim_host(struct mmc_host *host, >> struct mmc_ctx *ctx, } EXPORT_SYMBOL(__mmc_claim_host); >> >> +#if IS_ENABLED(CONFIG_MMC_PSTORE) >> +/** >> + * mmc_claim_host_async - claim host in atomic context >> + * @host: mmc host to claim >> + * >> + * This routine may be called in panic/oops scenarios. >> + * Return zero with host claim success, else busy status. >> + */ >> +int mmc_claim_host_async(struct mmc_host *host) { >> + if (!host->claimed && pm_runtime_active(mmc_dev(host))) { >> + host->claimed = 1; >> + return 0; >> + } >> + >> + return -EBUSY; >> +} >> +EXPORT_SYMBOL(mmc_claim_host_async); >> +#endif >> + >> /** >> * mmc_release_host - release a host >> * @host: mmc host to release >> diff --git a/drivers/mmc/core/mmcpstore.c >> b/drivers/mmc/core/mmcpstore.c new file mode 100644 index >> 000000000000..d7abe82612cb >> --- /dev/null >> +++ b/drivers/mmc/core/mmcpstore.c >> @@ -0,0 +1,310 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * MMC pstore support based on pstore/blk >> + * >> + * Copyright (c) 2020 Marvell. >> + * Author: Bhaskara Budiredla <bbudiredla@xxxxxxxxxxx> */ >> + >> +#define pr_fmt(fmt) "mmcpstore: " fmt >> + >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/pstore_blk.h> >> +#include <linux/blkdev.h> >> +#include <linux/mount.h> >> +#include <linux/slab.h> >> +#include <linux/mmc/mmc.h> >> +#include <linux/mmc/host.h> >> +#include <linux/mmc/card.h> >> +#include <linux/scatterlist.h> >> +#include "block.h" >> +#include "card.h" >> +#include "core.h" >> + >> +static struct mmcpstore_context { >> + char dev_name[BDEVNAME_SIZE]; >> + int partno; >> + sector_t start_sect; >> + sector_t size; >> + struct pstore_device_info dev; >> + struct pstore_blk_config conf; >> + struct pstore_blk_info info; >> + >> + char *sub; >> + struct mmc_card *card; >> + struct mmc_request *mrq; >> +} oops_cxt; >> + >> +static void mmc_prep_req(struct mmc_request *mrq, >> + unsigned int sect_offset, unsigned int nsects, >> + struct scatterlist *sg, u32 opcode, unsigned int >> +flags) { >> + mrq->cmd->opcode = opcode; >> + mrq->cmd->arg = sect_offset; >> + mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC; >> + >> + if (nsects == 1) { >> + mrq->stop = NULL; >> + } else { >> + mrq->stop->opcode = MMC_STOP_TRANSMISSION; >> + mrq->stop->arg = 0; >> + mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC; >> + } >> + >> + mrq->data->blksz = SECTOR_SIZE; >> + mrq->data->blocks = nsects; >> + mrq->data->flags = flags; >> + mrq->data->sg = sg; >> + mrq->data->sg_len = 1; >> +} >> + >> +static int mmcpstore_rdwr_req(const char *buf, unsigned int nsects, >> + unsigned int sect_offset, unsigned int flags) >> +{ >> + struct mmcpstore_context *cxt = &oops_cxt; >> + struct mmc_request *mrq = cxt->mrq; >> + struct mmc_card *card = cxt->card; >> + struct mmc_host *host = card->host; >> + struct scatterlist sg; >> + u32 opcode; >> + >> + if (flags == MMC_DATA_READ) >> + opcode = (nsects > 1) ? >> + MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; >> + else >> + opcode = (nsects > 1) ? >> + MMC_WRITE_MULTIPLE_BLOCK : MMC_WRITE_BLOCK; >> + >> + mmc_prep_req(mrq, sect_offset, nsects, &sg, opcode, flags); >> + sg_init_one(&sg, buf, (nsects << SECTOR_SHIFT)); >> + mmc_set_data_timeout(mrq->data, cxt->card); >> + >> + mmc_claim_host(host); >> + mmc_wait_for_req(host, mrq); >> + mdelay(mrq->data->timeout_ns / NSEC_PER_MSEC); >> + mmc_release_host(host); >> + >> + if (mrq->cmd->error) { >> + pr_err("Cmd error: %d\n", mrq->cmd->error); >> + return mrq->cmd->error; >> + } >> + if (mrq->data->error) { >> + pr_err("Data error: %d\n", mrq->data->error); >> + return mrq->data->error; >> + } >> + >> + return 0; >> +} >> + >> +static ssize_t mmcpstore_write(const char *buf, size_t size, loff_t >> +off) { >> + struct mmcpstore_context *cxt = &oops_cxt; >> + int ret; >> + >> + ret = mmcpstore_rdwr_req(buf, (size >> SECTOR_SHIFT), >> + cxt->start_sect + (off >> SECTOR_SHIFT), MMC_DATA_WRITE); >> + if (ret) >> + return ret; >> + >> + return size; >> +} >> + >> +static ssize_t mmcpstore_read(char *buf, size_t size, loff_t off) { >> + struct mmcpstore_context *cxt = &oops_cxt; >> + unsigned int sect_off = cxt->start_sect + (off >> SECTOR_SHIFT); >> + unsigned long sects = (cxt->conf.kmsg_size >> SECTOR_SHIFT); >> + int ret; >> + >> + if (unlikely(!buf || !size)) >> + return -EINVAL; >> + >> + ret = mmcpstore_rdwr_req(cxt->sub, sects, sect_off, >MMC_DATA_READ); >> + if (ret) >> + return ret; >> + memcpy(buf, cxt->sub, size); >> + >> + return size; >> +} >> + >> +static int mmcpstore_panic_write_req(const char *buf, >> + unsigned int nsects, unsigned int sect_offset) { >> + struct mmcpstore_context *cxt = &oops_cxt; >> + struct mmc_request *mrq = cxt->mrq; >> + struct mmc_card *card = cxt->card; >> + struct mmc_host *host = card->host; >> + struct scatterlist sg; >> + u32 opcode; >> + int ret; >> + >> + opcode = (nsects > 1) ? MMC_WRITE_MULTIPLE_BLOCK : >MMC_WRITE_BLOCK; >> + mmc_prep_req(mrq, sect_offset, nsects, &sg, opcode, >MMC_DATA_WRITE); >> + sg_init_one(&sg, buf, (nsects << SECTOR_SHIFT)); >> + mmc_set_data_timeout(mrq->data, cxt->card); >> + >> + ret = mmc_claim_host_async(host); >> + if (ret) >> + return ret; >> + >> + mmc_wait_for_pstore_req(host, mrq); >> + return 0; >> +} >> + >> +static ssize_t mmcpstore_panic_write(const char *buf, size_t size, >> +loff_t off) { >> + struct mmcpstore_context *cxt = &oops_cxt; >> + int ret; >> + >> + ret = mmcpstore_panic_write_req(buf, (size >> SECTOR_SHIFT), >> + cxt->start_sect + (off >> SECTOR_SHIFT)); >> + if (ret) >> + return ret; >> + >> + return size; >> +} >> + >> +static struct block_device *mmcpstore_open_backend(const char >> +*device) { >> + struct block_device *bdev; >> + dev_t devt; >> + >> + bdev = blkdev_get_by_path(device, FMODE_READ, NULL); >> + if (IS_ERR(bdev)) { >> + devt = name_to_dev_t(device); >> + if (devt == 0) >> + return ERR_PTR(-ENODEV); >> + >> + bdev = blkdev_get_by_dev(devt, FMODE_READ, NULL); >> + if (IS_ERR(bdev)) >> + return bdev; >> + } >> + >> + return bdev; >> +} >> + >> +static void mmcpstore_close_backend(struct block_device *bdev) { >> + if (!bdev) >> + return; >> + blkdev_put(bdev, FMODE_READ); >> +} >> + >> +void mmcpstore_card_set(struct mmc_card *card, const char *disk_name) >> +{ >> + struct mmcpstore_context *cxt = &oops_cxt; >> + struct pstore_blk_config *conf = &cxt->conf; >> + struct pstore_device_info *dev = &cxt->dev; >> + struct block_device *bdev; >> + struct mmc_command *stop; >> + struct mmc_command *cmd; >> + struct mmc_request *mrq; >> + struct mmc_data *data; >> + int ret; >> + >> + ret = pstore_blk_get_config(conf); >> + if (!conf->device[0]) { >> + pr_debug("psblk backend is empty\n"); >> + return; >> + } >> + >> + /* Multiple backend devices not allowed */ >> + if (cxt->dev_name[0]) >> + return; >> + >> + bdev = mmcpstore_open_backend(conf->device); >> + if (IS_ERR(bdev)) { >> + pr_err("%s failed to open with %ld\n", >> + conf->device, PTR_ERR(bdev)); >> + return; >> + } >> + >> + bdevname(bdev, cxt->dev_name); >> + cxt->partno = bdev->bd_part->partno; >> + mmcpstore_close_backend(bdev); >> + >> + if (strncmp(cxt->dev_name, disk_name, strlen(disk_name))) >> + return; >> + >> + cxt->start_sect = mmc_blk_get_part(card, cxt->partno, &cxt->size); >> + if (!cxt->start_sect) { >> + pr_err("Non-existent partition %d selected\n", cxt->partno); >> + return; >> + } >> + >> + /* Check for host mmc panic write polling function definitions */ >> + if (!card->host->ops->req_cleanup_pending || >> + !card->host->ops->req_completion_poll) >> + return; >> + >> + cxt->card = card; >> + >> + cxt->sub = kmalloc(conf->kmsg_size, GFP_KERNEL); >> + if (!cxt->sub) >> + goto out; >> + >> + mrq = kzalloc(sizeof(struct mmc_request), GFP_KERNEL); >> + if (!mrq) >> + goto free_sub; >> + >> + cmd = kzalloc(sizeof(struct mmc_command), GFP_KERNEL); >> + if (!cmd) >> + goto free_mrq; >> + >> + stop = kzalloc(sizeof(struct mmc_command), GFP_KERNEL); >> + if (!stop) >> + goto free_cmd; >> + >> + data = kzalloc(sizeof(struct mmc_data), GFP_KERNEL); >> + if (!data) >> + goto free_stop; >> + >> + mrq->cmd = cmd; >> + mrq->data = data; >> + mrq->stop = stop; >> + cxt->mrq = mrq; >> + >> + dev->total_size = cxt->size; >> + dev->flags = PSTORE_FLAGS_DMESG; >> + dev->read = mmcpstore_read; >> + dev->write = mmcpstore_write; >> + dev->erase = NULL; >> + dev->panic_write = mmcpstore_panic_write; >> + >> + ret = register_pstore_device(&cxt->dev); >> + if (ret) { >> + pr_err("%s registering with psblk failed (%d)\n", >> + cxt->dev_name, ret); >> + goto free_data; >> + } >> + >> + pr_info("%s registered as psblk backend\n", cxt->dev_name); >> + return; >> + >> +free_data: >> + kfree(data); >> +free_stop: >> + kfree(stop); >> +free_cmd: >> + kfree(cmd); >> +free_mrq: >> + kfree(mrq); >> +free_sub: >> + kfree(cxt->sub); >> +out: >> + return; >> +} >> + >> +void unregister_mmcpstore(void) >> +{ >> + struct mmcpstore_context *cxt = &oops_cxt; >> + >> + unregister_pstore_device(&cxt->dev); >> + kfree(cxt->mrq->data); >> + kfree(cxt->mrq->stop); >> + kfree(cxt->mrq->cmd); >> + kfree(cxt->mrq); >> + kfree(cxt->sub); >> + cxt->card = NULL; >> +} >> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index >> 29aa50711626..53840a361b5a 100644 >> --- a/include/linux/mmc/core.h >> +++ b/include/linux/mmc/core.h >> @@ -166,6 +166,11 @@ struct mmc_request { >> >> struct mmc_card; >> >> +#if IS_ENABLED(CONFIG_MMC_PSTORE) >> +void mmc_wait_for_pstore_req(struct mmc_host *host, struct >> +mmc_request *mrq); int mmc_claim_host_async(struct mmc_host *host); >> +#endif >> + >> void mmc_wait_for_req(struct mmc_host *host, struct mmc_request >> *mrq); int mmc_wait_for_cmd(struct mmc_host *host, struct >mmc_command *cmd, >> int retries); >> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index >> c079b932330f..7d6751005ac6 100644 >> --- a/include/linux/mmc/host.h >> +++ b/include/linux/mmc/host.h >> @@ -173,6 +173,18 @@ struct mmc_host_ops { >> */ >> int (*multi_io_quirk)(struct mmc_card *card, >> unsigned int direction, int >> blk_size); >> + >> +#if IS_ENABLED(CONFIG_MMC_PSTORE) >> + /* >> + * The following two APIs are introduced to support mmcpstore >> + * functionality. Cleanup API to terminate the ongoing and >> + * pending requests before a panic write post, and polling API >> + * to ensure that write succeeds before the Kernel dies. >> + */ >> + void (*req_cleanup_pending)(struct mmc_host *host); >> + int (*req_completion_poll)(struct mmc_host *host, >> + unsigned long timeout); #endif >> }; >> >> struct mmc_cqe_ops { >> -- >> 2.17.1 >>