[PATCH] spi: SPI_MASTER_MUST_* with scatter-gather only option and avoiding realloc

Mark Brown broonie at kernel.org
Tue May 19 05:46:32 PDT 2015


On Thu, May 14, 2015 at 09:58:44AM +0000, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>
> 
> Rewrite of spi_map_msg and spi_map_buf so that for SPI_MASTER_MUST_*:

This isn't just rewriting the internals, this is adding a completely new
API with separate code, fixing a bug, optimising the existing API and
adding a user of the new API.  I'd have expected all of that to be
called out in the changelog and for it to be split out into several
patches - especially the bug fix since that should go to Linus' tree and
stable.

Like I say I'm not entirely convinced we need the extra flags over just
using can_dma().

> It also fixes the insufficient cleanup in case __spi_map_msg returns
> an error.

This should be a separate patch.

> diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
> index ac1760e..ac74456 100644
> --- a/drivers/spi/spi-bcm2835.c
> +++ b/drivers/spi/spi-bcm2835.c
> @@ -463,7 +463,7 @@ void bcm2835_dma_init(struct spi_master *master, struct device *dev)
>  	master->can_dma = bcm2835_spi_can_dma;
>  	master->max_dma_len = 65535; /* limitation by BCM2835_SPI_DLEN */
>  	/* need to do TX AND RX DMA, so we need dummy buffers */
> -	master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
> +	master->flags = SPI_MASTER_MUST_RX_SG | SPI_MASTER_MUST_TX_SG;
>  
>  	return;
>  

This is updating a specific driver to use the new API you're adding,
this should be in a separate patch.

>  #ifdef CONFIG_HAS_DMA
> +static int spi_map_null(struct spi_master *master, struct device *dev,
> +			struct sg_table *sgt,
> +			struct sg_table *sgt_template,
> +			void *buf,
> +			enum dma_data_direction dir)
> +{

It's not obvious to me what this is supposed to do and it looks awfully
like it's duplicating the existing DMA mapping code (but if that's the
case the existing code should be using this not duplicating it so I
guess not).  I think it's supposed to be for creating a scatterlist that
repeatedly reused the same dummy page but there's this template thing,
it's getting a buffer passed in and...

> +	if (is_vmalloc_addr(buf)) {
> +		vm_page = vmalloc_to_page(buf);
> +		if (!vm_page) {
> +			sg_free_table(sgt);
> +			return -ENOMEM;
> +		}
> +		page_offset = offset_in_page(buf);
> +	} else {
> +		vm_page = NULL;
> +		page_offset = 0;
> +	}

...this is really weird for that case.  

I'd have expected to be allocating a scatterlist that transfers a
specified length to/from a normal page sized kmalloc() buffer in which
case we don't need to worry about vmalloc() or this template stuff.

> +		/*
> +		 * handle the SPI_MASTER_MUST_*_SG
> +		 * note that the situation with tx_buf and rx_buf both NULL
> +		 * is checked and handled inside spi_transfer_one_message
> +		 */
> +		if ((!xfer->tx_buf) && (xfer->rx_buf) &&
> +		    (master->flags & SPI_MASTER_MUST_TX_SG)) {
> +			ret = spi_map_null(master, tx_dev,
> +					   &xfer->tx_sg, &xfer->rx_sg,
> +					   master->page_tx,
> +					   DMA_TO_DEVICE);
> +			if (ret != 0) {
> +				spi_unmap_buf(master, rx_dev, &xfer->rx_sg,
> +					      DMA_FROM_DEVICE);
> +				return ret;
> +			}
> +		}

Why does the transmit handling use a scatterlist allocated for receive
(and vice versa)?  This goes back to the lack of clarity about what
spi_map_null() is doing.

>  		if (max_tx) {
> -			tmp = krealloc(master->dummy_tx, max_tx,
> -				       GFP_KERNEL | GFP_DMA);
> -			if (!tmp)
> -				return -ENOMEM;
> -			master->dummy_tx = tmp;
> -			memset(tmp, 0, max_tx);
> +			if (max_tx > PAGE_SIZE) {
> +				tmp_tx = krealloc(master->dummy_tx, max_tx,
> +						  GFP_KERNEL | GFP_DMA);
> +				if (!tmp_tx)
> +					return -ENOMEM;
> +				master->dummy_tx = tmp_tx;
> +				memset(tmp_tx, 0, max_tx);
> +			} else {
> +				tmp_tx = master->page_tx;
> +			}

This idea is fine but should be a separate patch.

I'd expect it should be about as cheap to change the realloc to be the
max of max_tx and PAGE_SIZE (the code was written on the basis that we'd
basically get that behaviour from the allocator anyway most of the
time though IIRC it will work in chunks smaller than a page) but this
does save us the memset() on the transmit path which is handy.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150519/19f70c44/attachment.sig>


More information about the linux-rpi-kernel mailing list