lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAPDyKFqMsMdqw=Uwzby0tNNvPieRT2i6PAmHu_9XRRVy1MykuQ@mail.gmail.com>
Date:   Tue, 15 Dec 2020 12:42:58 +0100
From:   Ulf Hansson <ulf.hansson@...aro.org>
To:     Bhaskara Budiredla <bbudiredla@...vell.com>
Cc:     Kees Cook <keescook@...omium.org>,
        Colin Cross <ccross@...roid.com>,
        Tony Luck <tony.luck@...el.com>,
        Sunil Kovvuri Goutham <sgoutham@...vell.com>,
        "linux-mmc@...r.kernel.org" <linux-mmc@...r.kernel.org>,
        Linux Kernel Mailing List <linux-kernel@...r.kernel.org>,
        Christoph Hellwig <hch@....de>
Subject: Re: [EXT] Re: [PATCH 1/2] mmc: Support kmsg dumper based on pstore/blk

On Tue, 15 Dec 2020 at 07:52, Bhaskara Budiredla <bbudiredla@...vell.com> wrote:
>
>
>
> >-----Original Message-----
> >From: Ulf Hansson <ulf.hansson@...aro.org>
> >Sent: Friday, December 11, 2020 5:02 PM
> >To: Bhaskara Budiredla <bbudiredla@...vell.com>
> >Cc: Kees Cook <keescook@...omium.org>; Colin Cross
> ><ccross@...roid.com>; Tony Luck <tony.luck@...el.com>; Sunil Kovvuri
> >Goutham <sgoutham@...vell.com>; linux-mmc@...r.kernel.org; Linux
> >Kernel Mailing List <linux-kernel@...r.kernel.org>; Christoph Hellwig
> ><hch@....de>
> >Subject: [EXT] Re: [PATCH 1/2] mmc: Support kmsg dumper based on
> >pstore/blk
> >
> >External Email
> >
> >----------------------------------------------------------------------
> >+ Christoph
> >
> >On Mon, 7 Dec 2020 at 12:58, Bhaskara Budiredla <bbudiredla@...vell.com>
> >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.
> >
> >Apologies for the delay. I have tried to give this some more thinking, more
> >comments below.
> >
> >[...]
> >
> >> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index
> >> d42037f0f10d..7682b267f1d5 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);
> >
> >So, the host driver should through this callback, be able to terminate any
> >ongoing requests/commands - and also try to make sure that the (e)MMC/SD
> >card remains in the data transfer state. Moreover, no locks and no IRQs must
> >be used to manage this, right?
> >
>
> Yes, that's correct.
>
> >Have you really tried if this works for real?
> >
>
> Yes, it's a working solution. Patch were submitted after testing.
>
> >> +       mmc_start_request(host, mrq);
> >
> >This looks like the wrong approach to me, as it will try to re-use the regular
> >request based path, where the host driver is allowed to use locks, IRQs,
> >DMAs, etc, etc.
> >
>
> No. The locks on host driver will be dropped in CONFIG_MMC_PSTORE path.
> Similarly, the IRQs will be replaced with polling subroutines during panic
> write. Please take a look at patch 2/2 which does this for cavium host driver.

Yes, but why is removing the locks okay for the other regular request case?

I assume the locks are there for a reason, right?

>
> >I would suggest inventing a new separate request path and a new host ops, to
> >deal with these kinds of requests.
> >
>
> The polling and cleanup host operations are the ones invented for this purpose.
> Polling to replace IRQs and cleanup to terminate ongoing requests/commands.

I understand the approach you have taken, but what I am saying is that
I don't think it is the best one to choose.

Then I think it's better to invent an entirely new path, rather than
trying to tweak the existing one. For example, in your case you could
leave the lock to be used (in patch2) for the existing regular request
path, but bypass the locks and use polling in the new one.

>
> >In this way, the below part with host->ops->req_completion_poll, can
> >probably be removed. Instead we may just wait for the request to return
> >from the new request path.
> >
>
> This is not possible unless CPU itself does the mmc write (through some block interface?).
> If the answer is block interface, sleeping cannot be avoided as part of it.

I wasn't thinking of the block interface, but rather of the internal
behavior of an mmc host driver.

For data transfers, it's common to use DMA when that is supported. If
DMA doesn't work, it's probably a FIFO being read/written to based
upon some IRQs telling when read/write should be done. Of course,
there are other cases as well.

For commands, I expect those to be managed through IRQs.

That said, all these things are preferably managed through a polling
based method instead, when doing the panic writes, correct?

> Do you really think DMA can be avoided for MMC panic writes?

That depends on the HW.

Certainly there are lots of cases where DMA isn't the only way, but
instead it's perfectly possible to fall back to polling based mode.

>
> >> +
> >> +       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
> >> diff --git a/drivers/mmc/core/mmcpstore.c
> >> b/drivers/mmc/core/mmcpstore.c new file mode 100644 index
> >> 000000000000..1113eae0756c
> >> --- /dev/null
> >> +++ b/drivers/mmc/core/mmcpstore.c
> >> @@ -0,0 +1,302 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * MMC pstore support based on pstore/blk
> >> + *
> >> + * Copyright (c) 2020 Marvell.
> >> + * Author: Bhaskara Budiredla <bbudiredla@...vell.com>  */
> >> +
> >> +#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;
> >> +}
> >
> >It looks like the above I/O read/write interface for pstore is intended to be
> >used when the platform is up and running and not during a panic, correct?
> >
> >If so, I don't get why it can't use the regular block interface, as any other file
> >system does, for example?
> >
>
> The pstore read and write operations are used as part of pstore file system mounting
> to retrieve the stored logs from MMC platform backend and to manage pstore read/write
> counters. Sleeping would be allowed during this time. Whereas, pstore PANIC write will be
> called if there happens a crash in the system. Sleeping is NOT allowed at this time.
>
> It seems you are mixing the sleeping paths of the mmcpstore with that of atomic path.

No, I am not mixing them, but questioning them.

For the non atomic path, I don't understand why the pstore file system
mounting, etc, deserves to be managed through its own specific ops?
Are there any specific reasons for this that I am missing?

In principle, for non atomic path, I would rather see that the pstore
file system should be able to be mounted on top of any generic block
device partition - without requiring the block device driver to
implement specific pstore ops.

>
>
> >> +
> >> +static void 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;
> >> +
> >> +       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);
> >> +
> >> +       mmc_claim_host(host);
> >
> >So, this will use several locks, which may be a problem, right?
> >
>
> No, as said above locks are present on host driver will be dropped
> in CONFIG_MMC_PSTORE path.

Please have a look at the code implementing mmc_claim_host(). It's not
just a simple spin_lock, but there is also a wait_queue and runtime PM
being managed from there.

>
> >Moreover, if there is an ongoing I/O request (or any other active
> >command/request for that matter), then the host is already claimed by the
> >mmc core. Normally, we would then wait for that request to be completed, to
> >trigger the release of the host and then allow us to claim it here.
> >
> >However, because of the kernel panic, I assume it's quite likely that any
> >ongoing request will not be completed at all, as IRQs may not work, for
> >example.
> >
> >In other words, we may be hanging here forever waiting to claim the host.
> >Unless we are lucky, because of no ongoing request, although we would still
> >have to succeed walking through all the locking, etc, in mmc_claim_host().
> >
>
> host->ops->req_cleanup_pending(host) was introduced to clean up the queued
> and ongoing requests/commands. Terminating ongoing requests is not a complicated
> thing for the host drivers.

Well, I don't agree. Resetting the host controller should not be a big
problem, but I am more worried about what state this will bring the
eMMC/SD card in.

It sounds to me that the only option is to try to rely on the
mmc_claim_host() to actually succeed. This makes it certain that there
is no ongoing request that needs to be terminated. Otherwise, things
will just fall apart.

The question is, can/should we rely on mmc_claim_host() to succeed in
this path? Maybe it will work, in cases when there is no ongoing
request, as it means the host should be available to be immediately
claimed. Although, then the problem ends up with runtime PM, as if the
host is available for claiming, it's likely that the host is runtime
suspended...

>
>
> >Do note, as part of the mmc_claim_host() we may also runtime resume the
> >host, if it was runtime suspended (which is quite likely). To runtime resume a
> >host via runtime PM, we may end up ungating device clocks, power on
> >corresponding PM domains, run a so called re-tuning sequence to restore
> >communication with the card, etc, etc. The point is, all these things must also
> >be possible to do, without locks and by using a polling based "mode"...
> >
> >> +       mmc_wait_for_pstore_req(host, mrq);
> >
> >Okay, so let's assume that we are lucky and succeed to claim and runtime
> >resume the host above.
> >
> >Then we also need to runtime resume the card, as it may be runtime
> >suspended. In the "worst case", runtime resuming the card means a complete
> >re-initialization of it, covering a series of commands and turning on regulators,
> >for example.
> >
> >> +       mmc_release_host(card->host);
> >> +}
>
>
> All the above said things (runtime resuming host, clocks being updated, restoring
> communication with host) are valid even for the newly asked request path. They cannot
> be avoided as we have to terminate the ongoing requests to complete the panic write.

Exactly. That's why I wonder if it's really worth it to support the
panic writes at all.

I realize that the host driver you are working on doesn't support
runtime PM, so it's not a problem for you, but for many other cases I
assume it would be.

Perhaps we could check that runtime PM is disabled for the mmc host as
a pre-condition to support this feature?

>
> >> +
> >> +static ssize_t mmcpstore_panic_write(const char *buf, size_t size,
> >> +loff_t off) {
> >> +       struct mmcpstore_context *cxt = &oops_cxt;
> >> +
> >> +       mmcpstore_panic_write_req(buf, (size >> SECTOR_SHIFT),
> >> +                       cxt->start_sect + (off >> SECTOR_SHIFT));
> >> +       return size;
> >> +}
> >
> >[...]
> >
> >Having said the above, I am not entirely convinced that it makes sense to
> >support this, at all.
> >
> >Not only, will the support be highly fragile from the mmc core point of view,
> >but there is also a significant complexity for an mmc host driver to support this
> >(at least in general).
> >
>
> I am not sure if the comments on host driver complexity is true. Terminating
> ongoing requests and introducing polling functions on host drivers should be
> straight forward. None those would disturb the core functionality. They are
> completely independent.

I think you are underestimating the part with terminating ongoing
requests. It sounds to me that you really haven't been terminating any
requests at all, but rather just doing a reset of the mmc controller
(which is what I observed in patch2).

When it comes to adding the new host ops to support the polling
functions, I have in principle no objections to that.

Kind regards
Uffe

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ