From: Artem Bityutskiy on
On Tue, 2010-04-13 at 13:31 +0200, Anders Larsen wrote:
> Tweak MTD's cache allocation to make it work with the atmel DMA'ed SPI.
> Substitute kmalloc for vmalloc so the cache buffer is mappable as per
> the Atmel SPI driver's requirements, otherwise an Oops would occur.
>
> The original patch by Ian McDonnell <ian(a)brightstareng.com> was found here:
> http://lists.infradead.org/pipermail/linux-mtd/2007-December/020184.html
>
> Signed-off-by: Anders Larsen <al(a)alarsen.net>
> Cc: Ian McDonnell <ian(a)brightstareng.com>
> Cc: David Woodhouse <dwmw2(a)infradead.org>
> Cc: Matthias Kaehlcke <matthias(a)kaehlcke.net>
> Cc: Artem Bityutskiy <Artem.Bityutskiy(a)nokia.com>
> Cc: Nicolas Pitre <nico(a)fluxnic.net>
> ---
> drivers/mtd/mtdblock.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> Index: b/drivers/mtd/mtdblock.c
> ===================================================================
> --- a/drivers/mtd/mtdblock.c
> +++ b/drivers/mtd/mtdblock.c
> @@ -253,7 +253,11 @@ static int mtdblock_writesect(struct mtd
> {
> struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
> if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {
> +#ifdef CONFIG_SPI_ATMEL
> + mtdblk->cache_data = kmalloc(mtdblk->mtd->erasesize, GFP_KERNEL);
> +#else
> mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
> +#endif
> if (!mtdblk->cache_data)
> return -EINTR;
> /* -EINTR is not really correct, but it is the best match
> @@ -322,7 +326,11 @@ static int mtdblock_release(struct mtd_b
> mtdblks[dev] = NULL;
> if (mtdblk->mtd->sync)
> mtdblk->mtd->sync(mtdblk->mtd);
> +#ifdef CONFIG_SPI_ATMEL
> + kfree(mtdblk->cache_data);
> +#else
> vfree(mtdblk->cache_data);
> +#endif
> kfree(mtdblk);
> }

This is an old problem. Instead of doing this dirty hack, change the
code and teach it to work with array of 1-4 pages , not with buffers.

--
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Anders Larsen on
On 2010-04-22 00:24:10, Andrew Morton wrote:
> Finally.. Wouldn't it be better to just fix the atmel SPI driver so
> that it doesn't barf when handed vmalloc'ed memory? Who do we ridicule
> about that? <checks, adds cc>

You mean something like this instead?

diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index c4e0442..a9ad5e8 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -352,16 +352,30 @@ atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)

xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
if (xfer->tx_buf) {
- xfer->tx_dma = dma_map_single(dev,
- (void *) xfer->tx_buf, xfer->len,
- DMA_TO_DEVICE);
+ if (is_vmalloc_addr(xfer->tx_buf))
+ xfer->tx_dma = dma_map_page(dev,
+ vmalloc_to_page(xfer->tx_buf),
+ (unsigned long)xfer->tx_buf & (PAGE_SIZE-1),
+ xfer->len,
+ DMA_TO_DEVICE);
+ else
+ xfer->tx_dma = dma_map_single(dev,
+ (void *) xfer->tx_buf, xfer->len,
+ DMA_TO_DEVICE);
if (dma_mapping_error(dev, xfer->tx_dma))
return -ENOMEM;
}
if (xfer->rx_buf) {
- xfer->rx_dma = dma_map_single(dev,
- xfer->rx_buf, xfer->len,
- DMA_FROM_DEVICE);
+ if (is_vmalloc_addr(xfer->rx_buf))
+ xfer->rx_dma = dma_map_page(dev,
+ vmalloc_to_page(xfer->rx_buf),
+ (unsigned long)xfer->rx_buf & (PAGE_SIZE-1),
+ xfer->len,
+ DMA_FROM_DEVICE);
+ else
+ xfer->rx_dma = dma_map_single(dev,
+ xfer->rx_buf, xfer->len,
+ DMA_FROM_DEVICE);
if (dma_mapping_error(dev, xfer->rx_dma)) {
if (xfer->tx_buf)
dma_unmap_single(dev,

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Andrew Morton on
On Wed, 19 May 2010 13:05:00 +0200
Anders Larsen <al(a)alarsen.net> wrote:

> On 2010-04-22 00:24:10, Andrew Morton wrote:
> > Finally.. Wouldn't it be better to just fix the atmel SPI driver so
> > that it doesn't barf when handed vmalloc'ed memory? Who do we ridicule
> > about that? <checks, adds cc>
>
> You mean something like this instead?

That looks simple enough. How do we get it tested, changelogged and
merged up? Haavard, can you please take a look?


> diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
> index c4e0442..a9ad5e8 100644
> --- a/drivers/spi/atmel_spi.c
> +++ b/drivers/spi/atmel_spi.c
> @@ -352,16 +352,30 @@ atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)
>
> xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
> if (xfer->tx_buf) {
> - xfer->tx_dma = dma_map_single(dev,
> - (void *) xfer->tx_buf, xfer->len,
> - DMA_TO_DEVICE);
> + if (is_vmalloc_addr(xfer->tx_buf))
> + xfer->tx_dma = dma_map_page(dev,
> + vmalloc_to_page(xfer->tx_buf),
> + (unsigned long)xfer->tx_buf & (PAGE_SIZE-1),
> + xfer->len,
> + DMA_TO_DEVICE);
> + else
> + xfer->tx_dma = dma_map_single(dev,
> + (void *) xfer->tx_buf, xfer->len,
> + DMA_TO_DEVICE);
> if (dma_mapping_error(dev, xfer->tx_dma))
> return -ENOMEM;
> }
> if (xfer->rx_buf) {
> - xfer->rx_dma = dma_map_single(dev,
> - xfer->rx_buf, xfer->len,
> - DMA_FROM_DEVICE);
> + if (is_vmalloc_addr(xfer->rx_buf))
> + xfer->rx_dma = dma_map_page(dev,
> + vmalloc_to_page(xfer->rx_buf),
> + (unsigned long)xfer->rx_buf & (PAGE_SIZE-1),
> + xfer->len,
> + DMA_FROM_DEVICE);
> + else
> + xfer->rx_dma = dma_map_single(dev,
> + xfer->rx_buf, xfer->len,
> + DMA_FROM_DEVICE);
> if (dma_mapping_error(dev, xfer->rx_dma)) {
> if (xfer->tx_buf)
> dma_unmap_single(dev,
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Ian McDonnell on
Anders,

I just tested one path, the "if (xfer->rx_buf)...", on
2.6.33 plus the at91 patch
http://maxim.org.za/AT91RM9200/2.6/2.6.33-at91.patch.gz
running on AT91SAM9260.

The test case involved doing i/o via the /dev/mtdblock
interface -- but this only exercises the rx_buf/vmalloc path --
MTD reads a block into the cache-buf to merge the write data. Not
sure that we have any use cases for the tx_buf path using MTD.

-Ian

On Friday 21 May 2010, Andrew Morton wrote:
> On Wed, 19 May 2010 13:05:00 +0200
>
> Anders Larsen <al(a)alarsen.net> wrote:
> > On 2010-04-22 00:24:10, Andrew Morton wrote:
> > > Finally.. Wouldn't it be better to just fix the atmel SPI
> > > driver so that it doesn't barf when handed vmalloc'ed
> > > memory? Who do we ridicule about that? <checks, adds cc>
> >
> > You mean something like this instead?
>
> That looks simple enough. How do we get it tested,
> changelogged and merged up? Haavard, can you please take a
> look?
>
> > diff --git a/drivers/spi/atmel_spi.c
> > b/drivers/spi/atmel_spi.c index c4e0442..a9ad5e8 100644
> > --- a/drivers/spi/atmel_spi.c
> > +++ b/drivers/spi/atmel_spi.c
> > @@ -352,16 +352,30 @@ atmel_spi_dma_map_xfer(struct
> > atmel_spi *as, struct spi_transfer *xfer)
> >
> > xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
> > if (xfer->tx_buf) {
> > - xfer->tx_dma = dma_map_single(dev,
> > - (void *) xfer->tx_buf, xfer->len,
> > - DMA_TO_DEVICE);
> > + if (is_vmalloc_addr(xfer->tx_buf))
> > + xfer->tx_dma = dma_map_page(dev,
> > + vmalloc_to_page(xfer->tx_buf),
> > + (unsigned long)xfer->tx_buf & (PAGE_SIZE-1),
> > + xfer->len,
> > + DMA_TO_DEVICE);
> > + else
> > + xfer->tx_dma = dma_map_single(dev,
> > + (void *) xfer->tx_buf, xfer->len,
> > + DMA_TO_DEVICE);
> > if (dma_mapping_error(dev, xfer->tx_dma))
> > return -ENOMEM;
> > }
> > if (xfer->rx_buf) {
> > - xfer->rx_dma = dma_map_single(dev,
> > - xfer->rx_buf, xfer->len,
> > - DMA_FROM_DEVICE);
> > + if (is_vmalloc_addr(xfer->rx_buf))
> > + xfer->rx_dma = dma_map_page(dev,
> > + vmalloc_to_page(xfer->rx_buf),
> > + (unsigned long)xfer->rx_buf & (PAGE_SIZE-1),
> > + xfer->len,
> > + DMA_FROM_DEVICE);
> > + else
> > + xfer->rx_dma = dma_map_single(dev,
> > + xfer->rx_buf, xfer->len,
> > + DMA_FROM_DEVICE);
> > if (dma_mapping_error(dev, xfer->rx_dma)) {
> > if (xfer->tx_buf)
> > dma_unmap_single(dev,


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Haavard Skinnemoen on
Andrew Morton <akpm(a)linux-foundation.org> wrote:
> On Wed, 19 May 2010 13:05:00 +0200
> Anders Larsen <al(a)alarsen.net> wrote:
>
> > On 2010-04-22 00:24:10, Andrew Morton wrote:
> > > Finally.. Wouldn't it be better to just fix the atmel SPI driver so
> > > that it doesn't barf when handed vmalloc'ed memory? Who do we ridicule
> > > about that? <checks, adds cc>
> >
> > You mean something like this instead?
>
> That looks simple enough. How do we get it tested, changelogged and
> merged up? Haavard, can you please take a look?

Sure. Sorry for the late response; I've been traveling for the last two
weeks.

Did anyone check what other drivers do to handle this case? Surely this
isn't the only driver which supports DMA?

> > diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
> > index c4e0442..a9ad5e8 100644
> > --- a/drivers/spi/atmel_spi.c
> > +++ b/drivers/spi/atmel_spi.c
> > @@ -352,16 +352,30 @@ atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)
> >
> > xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
> > if (xfer->tx_buf) {
> > - xfer->tx_dma = dma_map_single(dev,
> > - (void *) xfer->tx_buf, xfer->len,
> > - DMA_TO_DEVICE);
> > + if (is_vmalloc_addr(xfer->tx_buf))
> > + xfer->tx_dma = dma_map_page(dev,
> > + vmalloc_to_page(xfer->tx_buf),
> > + (unsigned long)xfer->tx_buf & (PAGE_SIZE-1),
> > + xfer->len,
> > + DMA_TO_DEVICE);

Ok, this should be fine for small transfers, but what happens if the
transfer crosses a page boundary? Are there any guarantees that this
will never happen? What callers are passing vmalloc'ed memory in the
first place?

Ditto for the rx path.

Haavard
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/