linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms
@ 2018-09-11 16:30 Wolfram Sang
  2018-09-11 16:30 ` [RFC PATCH 1/2] " Wolfram Sang
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Wolfram Sang @ 2018-09-11 16:30 UTC (permalink / raw)
  To: iommu, Robin Murphy
  Cc: linux-renesas-soc, Christoph Hellwig, Marek Szyprowski,
	linux-kernel, Wolfram Sang

Hi all,

commit 78c47830a5cb ("dma-debug: check scatterlist segments") triggers
for Renesas hardware I look after, so thanks for pointing out we should
have proper dma_parms for our DMA providers.

When trying to fix it, I became a bit puzzled about the life cycle of
the pointer to dma_parms. AFAIU most drivers leave the pointer dangling
on driver unbind. Check drivers/dma/bcm2835-dma.c, for example:

	od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
	if (!od)
		return -ENOMEM;

	pdev->dev.dma_parms = &od->dma_parms;
	dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);

And that's all about handling dma_parms. So, on unbind, the memory for
'od' gets freed and dma_params is a dangling pointer.

drivers/gpu/drm/exynos/exynos_drm_iommu.c seems to do it correctly:

static inline int configure_dma_max_seg_size(struct device *dev)
{
        if (!dev->dma_parms)
                dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL);
        if (!dev->dma_parms)
                return -ENOMEM;

        dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
        return 0;
}

static inline void clear_dma_max_seg_size(struct device *dev)
{
        kfree(dev->dma_parms);
        dev->dma_parms = NULL;
}

But this seems error prone and quite some code to add for every DMA
provider. So, I wondered if we couldn't have a helper for that. After
some brainstorming, I favour a dmam_-type of function. It will ensure
the memory gets freed and the pointer cleared on unbind. And it should
be easy to use.

I attached an RFC which I tested on a Renesas R-Car H3 SoC with the internal
DMAC of the SD controller. A branch can be found here (still waiting for
buildbot results):

git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/sdhi/set_max_seg

I added the companion function dmam_free_dma_parms() for completeness
although there is no user yet. I'd be totally open to drop it until
someone needs it.

Please let me know what you think. If this is the right track, I'll be
willing to fix the dangling pointers with it, too.

Thanks and happy hacking,

   Wolfram



Wolfram Sang (2):
  dma-mapping: introduce helper for setting dma_parms
  mmc: sdhi: internal_dmac: set dma_parms

 drivers/mmc/host/renesas_sdhi_internal_dmac.c |  2 +
 include/linux/dma-mapping.h                   |  5 ++
 kernel/dma/mapping.c                          | 50 +++++++++++++++++++
 3 files changed, 57 insertions(+)

-- 
2.18.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC PATCH 1/2] dma-mapping: introduce helper for setting dma_parms
  2018-09-11 16:30 [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Wolfram Sang
@ 2018-09-11 16:30 ` Wolfram Sang
  2018-09-11 16:30 ` [RFC PATCH 2/2] mmc: sdhi: internal_dmac: set dma_parms Wolfram Sang
  2018-09-11 17:24 ` [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Robin Murphy
  2 siblings, 0 replies; 5+ messages in thread
From: Wolfram Sang @ 2018-09-11 16:30 UTC (permalink / raw)
  To: iommu, Robin Murphy
  Cc: linux-renesas-soc, Christoph Hellwig, Marek Szyprowski,
	linux-kernel, Wolfram Sang

Setting up dma_parms seems not so trivial because it is easy to miss
that its pointer has the life cycle of its containing 'struct device'
while the allocation of dma_parms usually happens during the bind/unbind
life cycle. Which can lead to dangling pointers.
If coding this correctly in drivers, this results in boilerplate code.
So, this patch adds a devm_* style helper which is easy to use and make
sure the allocation and the pointer are always handled at the same time.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 include/linux/dma-mapping.h |  5 ++++
 kernel/dma/mapping.c        | 50 +++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 1db6a6b46d0d..05a525b4639b 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -700,6 +700,11 @@ static inline int dma_set_seg_boundary(struct device *dev, unsigned long mask)
 	return -EIO;
 }
 
+extern int dmam_set_dma_parms(struct device *dev, unsigned int max_seg_size,
+			      unsigned long seg_bound_mask);
+
+extern void dmam_free_dma_parms(struct device *dev);
+
 #ifndef dma_max_pfn
 static inline unsigned long dma_max_pfn(struct device *dev)
 {
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index d2a92ddaac4d..082cc651513b 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -198,6 +198,56 @@ EXPORT_SYMBOL(dmam_release_declared_memory);
 
 #endif
 
+static void dmam_release_dma_parms(struct device *dev, void *res)
+{
+	dev->dma_parms = NULL;
+}
+
+static int dmam_match_dma_parms(struct device *dev, void *res, void *data)
+{
+	return res == data;
+}
+
+/**
+ * dmam_set_dma_parms - Managed setting of dma_parms
+ * @dev: device to set dma_parms for
+ * @max_seg_size: the maximum segment size for this device
+ * @seg_bound_mask: the segment boundary mask for this device
+ *
+ * RETURNS:
+ * 0 on success, errno on failure.
+ */
+int dmam_set_dma_parms(struct device *dev, unsigned int max_seg_size,
+		       unsigned long seg_bound_mask)
+{
+	struct device_dma_parameters *parms;
+
+	parms = devres_alloc(dmam_release_dma_parms,
+			     sizeof(struct device_dma_parameters), GFP_KERNEL);
+	if (!parms)
+		return -ENOMEM;
+
+	dev->dma_parms = parms;
+	dma_set_max_seg_size(dev, max_seg_size);
+	dma_set_seg_boundary(dev, seg_bound_mask);
+
+	devres_add(dev, parms);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dmam_set_dma_parms);
+
+/**
+ * dmam_free_dma_parms - free dma_parms allocated with dmam_set_dma_parms
+ * @dev: device with dma_parms allocated by dmam_set_dma_parms()
+ */
+void dmam_free_dma_parms(struct device *dev)
+{
+	int rc = devres_destroy(dev, dmam_release_dma_parms, dmam_match_dma_parms,
+				dev->dma_parms);
+	WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(dmam_free_dma_parms);
+
 /*
  * Create scatter-list for the already allocated DMA buffer.
  */
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 2/2] mmc: sdhi: internal_dmac: set dma_parms
  2018-09-11 16:30 [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Wolfram Sang
  2018-09-11 16:30 ` [RFC PATCH 1/2] " Wolfram Sang
@ 2018-09-11 16:30 ` Wolfram Sang
  2018-09-11 17:24 ` [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Robin Murphy
  2 siblings, 0 replies; 5+ messages in thread
From: Wolfram Sang @ 2018-09-11 16:30 UTC (permalink / raw)
  To: iommu, Robin Murphy
  Cc: linux-renesas-soc, Christoph Hellwig, Marek Szyprowski,
	linux-kernel, Wolfram Sang

We have our own DMA provider, so we should set the dma_parms properly.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 drivers/mmc/host/renesas_sdhi_internal_dmac.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index ca0b43973769..00116e4ec6c4 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -315,6 +315,8 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
 
 	global_flags |= (unsigned long)soc->data;
 
+	dmam_set_dma_parms(&pdev->dev, 0xffffffff, 0);
+
 	return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops);
 }
 
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms
  2018-09-11 16:30 [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Wolfram Sang
  2018-09-11 16:30 ` [RFC PATCH 1/2] " Wolfram Sang
  2018-09-11 16:30 ` [RFC PATCH 2/2] mmc: sdhi: internal_dmac: set dma_parms Wolfram Sang
@ 2018-09-11 17:24 ` Robin Murphy
  2018-09-11 23:39   ` Wolfram Sang
  2 siblings, 1 reply; 5+ messages in thread
From: Robin Murphy @ 2018-09-11 17:24 UTC (permalink / raw)
  To: Wolfram Sang, iommu
  Cc: linux-renesas-soc, Christoph Hellwig, Marek Szyprowski, linux-kernel

On 11/09/18 17:30, Wolfram Sang wrote:
> Hi all,
> 
> commit 78c47830a5cb ("dma-debug: check scatterlist segments") triggers
> for Renesas hardware I look after, so thanks for pointing out we should
> have proper dma_parms for our DMA providers.
> 
> When trying to fix it, I became a bit puzzled about the life cycle of
> the pointer to dma_parms. AFAIU most drivers leave the pointer dangling
> on driver unbind. Check drivers/dma/bcm2835-dma.c, for example:
> 
> 	od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
> 	if (!od)
> 		return -ENOMEM;
> 
> 	pdev->dev.dma_parms = &od->dma_parms;
> 	dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);
> 
> And that's all about handling dma_parms. So, on unbind, the memory for
> 'od' gets freed and dma_params is a dangling pointer.

That's the typical case - the dma_parms structure is simply embedded in 
some other private data which tends to have the appropriate lifetime 
anyway. I can't see that the dangling pointer is an issue when it's set 
unconditionally on probe and valid until remove, because anyone 
dereferencing dev->dma_parms when dev has no driver bound is doing 
something very very wrong anyway. I suppose we could zero it in 
__device_release_driver() for good measure though - shame we've found 
something dma_deconfigure() could have been useful for just after we 
killed it ;)

> 
> drivers/gpu/drm/exynos/exynos_drm_iommu.c seems to do it correctly:

...but could be even simpler if it was just included in 
exynos_drm_private. Realistically, I struggle to imagine a driver which 
would need to set dma_parms that didn't already have some other private 
state into which it could fit.

Note that ultimately we'd like to have a single structure to hold all 
the masks and other gubbins (per the very original intent of dma_parms), 
so there's a fair chance of this getting fundamentally rejigged at some 
point anyway.

Robin.

> static inline int configure_dma_max_seg_size(struct device *dev)
> {
>          if (!dev->dma_parms)
>                  dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL);
>          if (!dev->dma_parms)
>                  return -ENOMEM;
> 
>          dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
>          return 0;
> }
> 
> static inline void clear_dma_max_seg_size(struct device *dev)
> {
>          kfree(dev->dma_parms);
>          dev->dma_parms = NULL;
> }
> 
> But this seems error prone and quite some code to add for every DMA
> provider. So, I wondered if we couldn't have a helper for that. After
> some brainstorming, I favour a dmam_-type of function. It will ensure
> the memory gets freed and the pointer cleared on unbind. And it should
> be easy to use.
> 
> I attached an RFC which I tested on a Renesas R-Car H3 SoC with the internal
> DMAC of the SD controller. A branch can be found here (still waiting for
> buildbot results):
> 
> git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/sdhi/set_max_seg
> 
> I added the companion function dmam_free_dma_parms() for completeness
> although there is no user yet. I'd be totally open to drop it until
> someone needs it.
> 
> Please let me know what you think. If this is the right track, I'll be
> willing to fix the dangling pointers with it, too.
> 
> Thanks and happy hacking,
> 
>     Wolfram
> 
> 
> 
> Wolfram Sang (2):
>    dma-mapping: introduce helper for setting dma_parms
>    mmc: sdhi: internal_dmac: set dma_parms
> 
>   drivers/mmc/host/renesas_sdhi_internal_dmac.c |  2 +
>   include/linux/dma-mapping.h                   |  5 ++
>   kernel/dma/mapping.c                          | 50 +++++++++++++++++++
>   3 files changed, 57 insertions(+)
> 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms
  2018-09-11 17:24 ` [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Robin Murphy
@ 2018-09-11 23:39   ` Wolfram Sang
  0 siblings, 0 replies; 5+ messages in thread
From: Wolfram Sang @ 2018-09-11 23:39 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Wolfram Sang, iommu, linux-renesas-soc, Christoph Hellwig,
	Marek Szyprowski, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1859 bytes --]

Hi Robin,

> > 	od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
> > 	if (!od)
> > 		return -ENOMEM;
> > 
> > 	pdev->dev.dma_parms = &od->dma_parms;
> > 	dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);
> > 
> > And that's all about handling dma_parms. So, on unbind, the memory for
> > 'od' gets freed and dma_params is a dangling pointer.
> 
> That's the typical case - the dma_parms structure is simply embedded in some
> other private data which tends to have the appropriate lifetime anyway. I
> can't see that the dangling pointer is an issue when it's set
> unconditionally on probe and valid until remove, because anyone
> dereferencing dev->dma_parms when dev has no driver bound is doing something
> very very wrong anyway. I suppose we could zero it in
> __device_release_driver() for good measure though - shame we've found
> something dma_deconfigure() could have been useful for just after we killed
> it ;)

I see. Yes, I was aware that the misuse of this dangling pointer is
somewhat academical. Yet, it was easy to fix and clearing this pointer
is good programming practice, I'd say. I agree that clearing the pointer
in __device_release_driver is a good option, too, if documentation about
its expected life cycle (== get's cleared on unbind) is clear about
that. Probably that life cycle confusion led to the more complicated
code in the exynos_drm driver. I will look into all of that tomorrow.

> Note that ultimately we'd like to have a single structure to hold all the
> masks and other gubbins (per the very original intent of dma_parms), so

I was wondering about that, yes.

> there's a fair chance of this getting fundamentally rejigged at some point
> anyway.

Makes sense. Yet, as this change is gonna be small, I think it's still
nice to have.

Thanks for the input!

   Wolfram


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2018-09-11 23:39 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-11 16:30 [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Wolfram Sang
2018-09-11 16:30 ` [RFC PATCH 1/2] " Wolfram Sang
2018-09-11 16:30 ` [RFC PATCH 2/2] mmc: sdhi: internal_dmac: set dma_parms Wolfram Sang
2018-09-11 17:24 ` [RFC PATCH 0/2] dma-mapping: introduce helper for setting dma_parms Robin Murphy
2018-09-11 23:39   ` Wolfram Sang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).