2022-01-26 22:41:43

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH 01/19] dma-buf-map: Add read/write helpers

In certain situations it's useful to be able to read or write to an
offset that is calculated by having the memory layout given by a struct
declaration. Usually we are going to read/write a u8, u16, u32 or u64.

Add a pair of macros dma_buf_map_read_field()/dma_buf_map_write_field()
to calculate the offset of a struct member and memcpy the data from/to
the dma_buf_map. We could use readb, readw, readl, readq and the write*
counterparts, however due to alignment issues this may not work on all
architectures. If alignment needs to be checked to call the right
function, it's not possible to decide at compile-time which function to
call: so just leave the decision to the memcpy function that will do
exactly that on IO memory or dereference the pointer.

Cc: Sumit Semwal <[email protected]>
Cc: Christian König <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Lucas De Marchi <[email protected]>
---
include/linux/dma-buf-map.h | 81 +++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)

diff --git a/include/linux/dma-buf-map.h b/include/linux/dma-buf-map.h
index 19fa0b5ae5ec..65e927d9ce33 100644
--- a/include/linux/dma-buf-map.h
+++ b/include/linux/dma-buf-map.h
@@ -6,6 +6,7 @@
#ifndef __DMA_BUF_MAP_H__
#define __DMA_BUF_MAP_H__

+#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/string.h>

@@ -229,6 +230,46 @@ static inline void dma_buf_map_clear(struct dma_buf_map *map)
}
}

+/**
+ * dma_buf_map_memcpy_to_offset - Memcpy into offset of dma-buf mapping
+ * @dst: The dma-buf mapping structure
+ * @offset: The offset from which to copy
+ * @src: The source buffer
+ * @len: The number of byte in src
+ *
+ * Copies data into a dma-buf mapping with an offset. The source buffer is in
+ * system memory. Depending on the buffer's location, the helper picks the
+ * correct method of accessing the memory.
+ */
+static inline void dma_buf_map_memcpy_to_offset(struct dma_buf_map *dst, size_t offset,
+ const void *src, size_t len)
+{
+ if (dst->is_iomem)
+ memcpy_toio(dst->vaddr_iomem + offset, src, len);
+ else
+ memcpy(dst->vaddr + offset, src, len);
+}
+
+/**
+ * dma_buf_map_memcpy_from_offset - Memcpy from offset of dma-buf mapping into system memory
+ * @dst: Destination in system memory
+ * @src: The dma-buf mapping structure
+ * @src: The offset from which to copy
+ * @len: The number of byte in src
+ *
+ * Copies data from a dma-buf mapping with an offset. The dest buffer is in
+ * system memory. Depending on the mapping location, the helper picks the
+ * correct method of accessing the memory.
+ */
+static inline void dma_buf_map_memcpy_from_offset(void *dst, const struct dma_buf_map *src,
+ size_t offset, size_t len)
+{
+ if (src->is_iomem)
+ memcpy_fromio(dst, src->vaddr_iomem + offset, len);
+ else
+ memcpy(dst, src->vaddr + offset, len);
+}
+
/**
* dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
* @dst: The dma-buf mapping structure
@@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct dma_buf_map *map, size_t incr)
map->vaddr += incr;
}

+/**
+ * dma_buf_map_read_field - Read struct member from dma-buf mapping with
+ * arbitrary size and handling un-aligned accesses
+ *
+ * @map__: The dma-buf mapping structure
+ * @type__: The struct to be used containing the field to read
+ * @field__: Member from struct we want to read
+ *
+ * Read a value from dma-buf mapping calculating the offset and size: this assumes
+ * the dma-buf mapping is aligned with a a struct type__. A single u8, u16, u32
+ * or u64 can be read, based on the offset and size of type__.field__.
+ */
+#define dma_buf_map_read_field(map__, type__, field__) ({ \
+ type__ *t__; \
+ typeof(t__->field__) val__; \
+ dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__, field__), \
+ sizeof(t__->field__)); \
+ val__; \
+})
+
+/**
+ * dma_buf_map_write_field - Write struct member to the dma-buf mapping with
+ * arbitrary size and handling un-aligned accesses
+ *
+ * @map__: The dma-buf mapping structure
+ * @type__: The struct to be used containing the field to write
+ * @field__: Member from struct we want to write
+ * @val__: Value to be written
+ *
+ * Write a value to the dma-buf mapping calculating the offset and size.
+ * A single u8, u16, u32 or u64 can be written based on the offset and size of
+ * type__.field__.
+ */
+#define dma_buf_map_write_field(map__, type__, field__, val__) ({ \
+ type__ *t__; \
+ typeof(t__->field__) val____ = val__; \
+ dma_buf_map_memcpy_to_offset(map__, offsetof(type__, field__), \
+ &val____, sizeof(t__->field__)); \
+})
+
#endif /* __DMA_BUF_MAP_H__ */
--
2.35.0


2022-01-27 13:54:04

by Christian König

[permalink] [raw]
Subject: Re: [PATCH 01/19] dma-buf-map: Add read/write helpers

Am 26.01.22 um 21:36 schrieb Lucas De Marchi:
> In certain situations it's useful to be able to read or write to an
> offset that is calculated by having the memory layout given by a struct
> declaration. Usually we are going to read/write a u8, u16, u32 or u64.
>
> Add a pair of macros dma_buf_map_read_field()/dma_buf_map_write_field()
> to calculate the offset of a struct member and memcpy the data from/to
> the dma_buf_map. We could use readb, readw, readl, readq and the write*
> counterparts, however due to alignment issues this may not work on all
> architectures. If alignment needs to be checked to call the right
> function, it's not possible to decide at compile-time which function to
> call: so just leave the decision to the memcpy function that will do
> exactly that on IO memory or dereference the pointer.
>
> Cc: Sumit Semwal <[email protected]>
> Cc: Christian König <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Signed-off-by: Lucas De Marchi <[email protected]>
> ---
> include/linux/dma-buf-map.h | 81 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
>
> diff --git a/include/linux/dma-buf-map.h b/include/linux/dma-buf-map.h
> index 19fa0b5ae5ec..65e927d9ce33 100644
> --- a/include/linux/dma-buf-map.h
> +++ b/include/linux/dma-buf-map.h
> @@ -6,6 +6,7 @@
> #ifndef __DMA_BUF_MAP_H__
> #define __DMA_BUF_MAP_H__
>
> +#include <linux/kernel.h>
> #include <linux/io.h>
> #include <linux/string.h>
>
> @@ -229,6 +230,46 @@ static inline void dma_buf_map_clear(struct dma_buf_map *map)
> }
> }
>
> +/**
> + * dma_buf_map_memcpy_to_offset - Memcpy into offset of dma-buf mapping
> + * @dst: The dma-buf mapping structure
> + * @offset: The offset from which to copy
> + * @src: The source buffer
> + * @len: The number of byte in src
> + *
> + * Copies data into a dma-buf mapping with an offset. The source buffer is in
> + * system memory. Depending on the buffer's location, the helper picks the
> + * correct method of accessing the memory.
> + */
> +static inline void dma_buf_map_memcpy_to_offset(struct dma_buf_map *dst, size_t offset,
> + const void *src, size_t len)
> +{
> + if (dst->is_iomem)
> + memcpy_toio(dst->vaddr_iomem + offset, src, len);
> + else
> + memcpy(dst->vaddr + offset, src, len);
> +}
> +
> +/**
> + * dma_buf_map_memcpy_from_offset - Memcpy from offset of dma-buf mapping into system memory
> + * @dst: Destination in system memory
> + * @src: The dma-buf mapping structure
> + * @src: The offset from which to copy
> + * @len: The number of byte in src
> + *
> + * Copies data from a dma-buf mapping with an offset. The dest buffer is in
> + * system memory. Depending on the mapping location, the helper picks the
> + * correct method of accessing the memory.
> + */
> +static inline void dma_buf_map_memcpy_from_offset(void *dst, const struct dma_buf_map *src,
> + size_t offset, size_t len)
> +{
> + if (src->is_iomem)
> + memcpy_fromio(dst, src->vaddr_iomem + offset, len);
> + else
> + memcpy(dst, src->vaddr + offset, len);
> +}
> +

Well that's certainly a valid use case, but I suggest to change the
implementation of the existing functions to call the new ones with offset=0.

This way we only have one implementation.

> /**
> * dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
> * @dst: The dma-buf mapping structure
> @@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct dma_buf_map *map, size_t incr)
> map->vaddr += incr;
> }
>
> +/**
> + * dma_buf_map_read_field - Read struct member from dma-buf mapping with
> + * arbitrary size and handling un-aligned accesses
> + *
> + * @map__: The dma-buf mapping structure
> + * @type__: The struct to be used containing the field to read
> + * @field__: Member from struct we want to read
> + *
> + * Read a value from dma-buf mapping calculating the offset and size: this assumes
> + * the dma-buf mapping is aligned with a a struct type__. A single u8, u16, u32
> + * or u64 can be read, based on the offset and size of type__.field__.
> + */
> +#define dma_buf_map_read_field(map__, type__, field__) ({ \
> + type__ *t__; \
> + typeof(t__->field__) val__; \
> + dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__, field__), \
> + sizeof(t__->field__)); \
> + val__; \
> +})
> +
> +/**
> + * dma_buf_map_write_field - Write struct member to the dma-buf mapping with
> + * arbitrary size and handling un-aligned accesses
> + *
> + * @map__: The dma-buf mapping structure
> + * @type__: The struct to be used containing the field to write
> + * @field__: Member from struct we want to write
> + * @val__: Value to be written
> + *
> + * Write a value to the dma-buf mapping calculating the offset and size.
> + * A single u8, u16, u32 or u64 can be written based on the offset and size of
> + * type__.field__.
> + */
> +#define dma_buf_map_write_field(map__, type__, field__, val__) ({ \
> + type__ *t__; \
> + typeof(t__->field__) val____ = val__; \
> + dma_buf_map_memcpy_to_offset(map__, offsetof(type__, field__), \
> + &val____, sizeof(t__->field__)); \
> +})
> +

Uff well that absolutely looks like overkill to me.

That's a rather special use case as far as I can see and I think we
should only have this in the common framework if more than one driver is
using it.

Regards,
Christian.

> #endif /* __DMA_BUF_MAP_H__ */

2022-01-27 14:04:34

by Matthew Brost

[permalink] [raw]
Subject: Re: [PATCH 01/19] dma-buf-map: Add read/write helpers

On Thu, Jan 27, 2022 at 08:24:04AM +0100, Christian K?nig wrote:
> Am 26.01.22 um 21:36 schrieb Lucas De Marchi:
> > In certain situations it's useful to be able to read or write to an
> > offset that is calculated by having the memory layout given by a struct
> > declaration. Usually we are going to read/write a u8, u16, u32 or u64.
> >
> > Add a pair of macros dma_buf_map_read_field()/dma_buf_map_write_field()
> > to calculate the offset of a struct member and memcpy the data from/to
> > the dma_buf_map. We could use readb, readw, readl, readq and the write*
> > counterparts, however due to alignment issues this may not work on all
> > architectures. If alignment needs to be checked to call the right
> > function, it's not possible to decide at compile-time which function to
> > call: so just leave the decision to the memcpy function that will do
> > exactly that on IO memory or dereference the pointer.
> >
> > Cc: Sumit Semwal <[email protected]>
> > Cc: Christian K?nig <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: [email protected]
> > Signed-off-by: Lucas De Marchi <[email protected]>
> > ---
> > include/linux/dma-buf-map.h | 81 +++++++++++++++++++++++++++++++++++++
> > 1 file changed, 81 insertions(+)
> >
> > diff --git a/include/linux/dma-buf-map.h b/include/linux/dma-buf-map.h
> > index 19fa0b5ae5ec..65e927d9ce33 100644
> > --- a/include/linux/dma-buf-map.h
> > +++ b/include/linux/dma-buf-map.h
> > @@ -6,6 +6,7 @@
> > #ifndef __DMA_BUF_MAP_H__
> > #define __DMA_BUF_MAP_H__
> > +#include <linux/kernel.h>
> > #include <linux/io.h>
> > #include <linux/string.h>
> > @@ -229,6 +230,46 @@ static inline void dma_buf_map_clear(struct dma_buf_map *map)
> > }
> > }
> > +/**
> > + * dma_buf_map_memcpy_to_offset - Memcpy into offset of dma-buf mapping
> > + * @dst: The dma-buf mapping structure
> > + * @offset: The offset from which to copy
> > + * @src: The source buffer
> > + * @len: The number of byte in src
> > + *
> > + * Copies data into a dma-buf mapping with an offset. The source buffer is in
> > + * system memory. Depending on the buffer's location, the helper picks the
> > + * correct method of accessing the memory.
> > + */
> > +static inline void dma_buf_map_memcpy_to_offset(struct dma_buf_map *dst, size_t offset,
> > + const void *src, size_t len)
> > +{
> > + if (dst->is_iomem)
> > + memcpy_toio(dst->vaddr_iomem + offset, src, len);
> > + else
> > + memcpy(dst->vaddr + offset, src, len);
> > +}
> > +
> > +/**
> > + * dma_buf_map_memcpy_from_offset - Memcpy from offset of dma-buf mapping into system memory
> > + * @dst: Destination in system memory
> > + * @src: The dma-buf mapping structure
> > + * @src: The offset from which to copy
> > + * @len: The number of byte in src
> > + *
> > + * Copies data from a dma-buf mapping with an offset. The dest buffer is in
> > + * system memory. Depending on the mapping location, the helper picks the
> > + * correct method of accessing the memory.
> > + */
> > +static inline void dma_buf_map_memcpy_from_offset(void *dst, const struct dma_buf_map *src,
> > + size_t offset, size_t len)
> > +{
> > + if (src->is_iomem)
> > + memcpy_fromio(dst, src->vaddr_iomem + offset, len);
> > + else
> > + memcpy(dst, src->vaddr + offset, len);
> > +}
> > +
>
> Well that's certainly a valid use case, but I suggest to change the
> implementation of the existing functions to call the new ones with offset=0.
>
> This way we only have one implementation.
>
Trivial - but agree with Christian that is a good cleanup.

> > /**
> > * dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
> > * @dst: The dma-buf mapping structure
> > @@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct dma_buf_map *map, size_t incr)
> > map->vaddr += incr;
> > }
> > +/**
> > + * dma_buf_map_read_field - Read struct member from dma-buf mapping with
> > + * arbitrary size and handling un-aligned accesses
> > + *
> > + * @map__: The dma-buf mapping structure
> > + * @type__: The struct to be used containing the field to read
> > + * @field__: Member from struct we want to read
> > + *
> > + * Read a value from dma-buf mapping calculating the offset and size: this assumes
> > + * the dma-buf mapping is aligned with a a struct type__. A single u8, u16, u32
> > + * or u64 can be read, based on the offset and size of type__.field__.
> > + */
> > +#define dma_buf_map_read_field(map__, type__, field__) ({ \
> > + type__ *t__; \
> > + typeof(t__->field__) val__; \
> > + dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__, field__), \
> > + sizeof(t__->field__)); \
> > + val__; \
> > +})
> > +
> > +/**
> > + * dma_buf_map_write_field - Write struct member to the dma-buf mapping with
> > + * arbitrary size and handling un-aligned accesses
> > + *
> > + * @map__: The dma-buf mapping structure
> > + * @type__: The struct to be used containing the field to write
> > + * @field__: Member from struct we want to write
> > + * @val__: Value to be written
> > + *
> > + * Write a value to the dma-buf mapping calculating the offset and size.
> > + * A single u8, u16, u32 or u64 can be written based on the offset and size of
> > + * type__.field__.
> > + */
> > +#define dma_buf_map_write_field(map__, type__, field__, val__) ({ \
> > + type__ *t__; \
> > + typeof(t__->field__) val____ = val__; \
> > + dma_buf_map_memcpy_to_offset(map__, offsetof(type__, field__), \
> > + &val____, sizeof(t__->field__)); \
> > +})
> > +
>
> Uff well that absolutely looks like overkill to me.
>

Hold on...

> That's a rather special use case as far as I can see and I think we should
> only have this in the common framework if more than one driver is using it.
>

I disagree, this is rather elegant.

The i915 can't be the *only* driver that defines a struct which
describes the layout of a dma_buf object.

IMO this base macro allows *all* other drivers to build on this write
directly to fields in structures those drivers have defined. Patches
later in this series do this for the GuC ads.

Matt

> Regards,
> Christian.
>
> > #endif /* __DMA_BUF_MAP_H__ */
>

2022-01-27 14:16:03

by Christian König

[permalink] [raw]
Subject: Re: [PATCH 01/19] dma-buf-map: Add read/write helpers

Am 27.01.22 um 08:36 schrieb Matthew Brost:
> [SNIP]
>>> /**
>>> * dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
>>> * @dst: The dma-buf mapping structure
>>> @@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct dma_buf_map *map, size_t incr)
>>> map->vaddr += incr;
>>> }
>>> +/**
>>> + * dma_buf_map_read_field - Read struct member from dma-buf mapping with
>>> + * arbitrary size and handling un-aligned accesses
>>> + *
>>> + * @map__: The dma-buf mapping structure
>>> + * @type__: The struct to be used containing the field to read
>>> + * @field__: Member from struct we want to read
>>> + *
>>> + * Read a value from dma-buf mapping calculating the offset and size: this assumes
>>> + * the dma-buf mapping is aligned with a a struct type__. A single u8, u16, u32
>>> + * or u64 can be read, based on the offset and size of type__.field__.
>>> + */
>>> +#define dma_buf_map_read_field(map__, type__, field__) ({ \
>>> + type__ *t__; \
>>> + typeof(t__->field__) val__; \
>>> + dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__, field__), \
>>> + sizeof(t__->field__)); \
>>> + val__; \
>>> +})
>>> +
>>> +/**
>>> + * dma_buf_map_write_field - Write struct member to the dma-buf mapping with
>>> + * arbitrary size and handling un-aligned accesses
>>> + *
>>> + * @map__: The dma-buf mapping structure
>>> + * @type__: The struct to be used containing the field to write
>>> + * @field__: Member from struct we want to write
>>> + * @val__: Value to be written
>>> + *
>>> + * Write a value to the dma-buf mapping calculating the offset and size.
>>> + * A single u8, u16, u32 or u64 can be written based on the offset and size of
>>> + * type__.field__.
>>> + */
>>> +#define dma_buf_map_write_field(map__, type__, field__, val__) ({ \
>>> + type__ *t__; \
>>> + typeof(t__->field__) val____ = val__; \
>>> + dma_buf_map_memcpy_to_offset(map__, offsetof(type__, field__), \
>>> + &val____, sizeof(t__->field__)); \
>>> +})
>>> +
>> Uff well that absolutely looks like overkill to me.
>>
> Hold on...
>
>> That's a rather special use case as far as I can see and I think we should
>> only have this in the common framework if more than one driver is using it.
>>
> I disagree, this is rather elegant.
>
> The i915 can't be the *only* driver that defines a struct which
> describes the layout of a dma_buf object.

That's not the problem, amdgpu as well as nouveau are doing that as
well. The problem is DMA-buf is a buffer sharing framework between drivers.

In other words which importer is supposed to use this with a DMA-buf
exported by another device?

> IMO this base macro allows *all* other drivers to build on this write
> directly to fields in structures those drivers have defined.

Exactly that's the point. This is something drivers should absolutely
*NOT* do.

That are driver internals and it is extremely questionable to move this
into the common framework.

Regards,
Christian.

> Patches
> later in this series do this for the GuC ads.
>
> Matt
>
>> Regards,
>> Christian.
>>
>>> #endif /* __DMA_BUF_MAP_H__ */

2022-01-27 14:47:00

by Daniel Vetter

[permalink] [raw]
Subject: Re: [Intel-gfx] [PATCH 01/19] dma-buf-map: Add read/write helpers

On Thu, Jan 27, 2022 at 08:59:36AM +0100, Christian K?nig wrote:
> Am 27.01.22 um 08:36 schrieb Matthew Brost:
> > [SNIP]
> > > > /**
> > > > * dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
> > > > * @dst: The dma-buf mapping structure
> > > > @@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct dma_buf_map *map, size_t incr)
> > > > map->vaddr += incr;
> > > > }
> > > > +/**
> > > > + * dma_buf_map_read_field - Read struct member from dma-buf mapping with
> > > > + * arbitrary size and handling un-aligned accesses
> > > > + *
> > > > + * @map__: The dma-buf mapping structure
> > > > + * @type__: The struct to be used containing the field to read
> > > > + * @field__: Member from struct we want to read
> > > > + *
> > > > + * Read a value from dma-buf mapping calculating the offset and size: this assumes
> > > > + * the dma-buf mapping is aligned with a a struct type__. A single u8, u16, u32
> > > > + * or u64 can be read, based on the offset and size of type__.field__.
> > > > + */
> > > > +#define dma_buf_map_read_field(map__, type__, field__) ({ \
> > > > + type__ *t__; \
> > > > + typeof(t__->field__) val__; \
> > > > + dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__, field__), \
> > > > + sizeof(t__->field__)); \
> > > > + val__; \
> > > > +})
> > > > +
> > > > +/**
> > > > + * dma_buf_map_write_field - Write struct member to the dma-buf mapping with
> > > > + * arbitrary size and handling un-aligned accesses
> > > > + *
> > > > + * @map__: The dma-buf mapping structure
> > > > + * @type__: The struct to be used containing the field to write
> > > > + * @field__: Member from struct we want to write
> > > > + * @val__: Value to be written
> > > > + *
> > > > + * Write a value to the dma-buf mapping calculating the offset and size.
> > > > + * A single u8, u16, u32 or u64 can be written based on the offset and size of
> > > > + * type__.field__.
> > > > + */
> > > > +#define dma_buf_map_write_field(map__, type__, field__, val__) ({ \
> > > > + type__ *t__; \
> > > > + typeof(t__->field__) val____ = val__; \
> > > > + dma_buf_map_memcpy_to_offset(map__, offsetof(type__, field__), \
> > > > + &val____, sizeof(t__->field__)); \
> > > > +})
> > > > +
> > > Uff well that absolutely looks like overkill to me.
> > >
> > Hold on...
> >
> > > That's a rather special use case as far as I can see and I think we should
> > > only have this in the common framework if more than one driver is using it.
> > >
> > I disagree, this is rather elegant.
> >
> > The i915 can't be the *only* driver that defines a struct which
> > describes the layout of a dma_buf object.
>
> That's not the problem, amdgpu as well as nouveau are doing that as well.
> The problem is DMA-buf is a buffer sharing framework between drivers.
>
> In other words which importer is supposed to use this with a DMA-buf
> exported by another device?
>
> > IMO this base macro allows *all* other drivers to build on this write
> > directly to fields in structures those drivers have defined.
>
> Exactly that's the point. This is something drivers should absolutely *NOT*
> do.
>
> That are driver internals and it is extremely questionable to move this into
> the common framework.

See my other reply.

This is about struct dma_buf_map, which is just a tagged pointer.

Which happens to be used by the dma_buf cross-driver interface, but it's
also used plenty internally in buffer allocation helpers, fbdev,
everything else. And it was _meant_ to be used like that - this thing is
my idea, I know :-)

I guess we could move/rename it, but like I said I really don't have any
good ideas. Got some?
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

2022-01-28 05:30:25

by Thomas Zimmermann

[permalink] [raw]
Subject: Re: [PATCH 01/19] dma-buf-map: Add read/write helpers

Hi

Am 26.01.22 um 21:36 schrieb Lucas De Marchi:
> In certain situations it's useful to be able to read or write to an
> offset that is calculated by having the memory layout given by a struct
> declaration. Usually we are going to read/write a u8, u16, u32 or u64.
>
> Add a pair of macros dma_buf_map_read_field()/dma_buf_map_write_field()
> to calculate the offset of a struct member and memcpy the data from/to
> the dma_buf_map. We could use readb, readw, readl, readq and the write*
> counterparts, however due to alignment issues this may not work on all
> architectures. If alignment needs to be checked to call the right
> function, it's not possible to decide at compile-time which function to
> call: so just leave the decision to the memcpy function that will do
> exactly that on IO memory or dereference the pointer.
>
> Cc: Sumit Semwal <[email protected]>
> Cc: Christian König <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Signed-off-by: Lucas De Marchi <[email protected]>
> ---
> include/linux/dma-buf-map.h | 81 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
>
> diff --git a/include/linux/dma-buf-map.h b/include/linux/dma-buf-map.h
> index 19fa0b5ae5ec..65e927d9ce33 100644
> --- a/include/linux/dma-buf-map.h
> +++ b/include/linux/dma-buf-map.h
> @@ -6,6 +6,7 @@
> #ifndef __DMA_BUF_MAP_H__
> #define __DMA_BUF_MAP_H__
>
> +#include <linux/kernel.h>
> #include <linux/io.h>
> #include <linux/string.h>
>
> @@ -229,6 +230,46 @@ static inline void dma_buf_map_clear(struct dma_buf_map *map)
> }
> }
>
> +/**
> + * dma_buf_map_memcpy_to_offset - Memcpy into offset of dma-buf mapping
> + * @dst: The dma-buf mapping structure
> + * @offset: The offset from which to copy
> + * @src: The source buffer
> + * @len: The number of byte in src
> + *
> + * Copies data into a dma-buf mapping with an offset. The source buffer is in
> + * system memory. Depending on the buffer's location, the helper picks the
> + * correct method of accessing the memory.
> + */
> +static inline void dma_buf_map_memcpy_to_offset(struct dma_buf_map *dst, size_t offset,
> + const void *src, size_t len)
> +{
> + if (dst->is_iomem)
> + memcpy_toio(dst->vaddr_iomem + offset, src, len);
> + else
> + memcpy(dst->vaddr + offset, src, len);
> +}

Please don't add a new function. Rather please add the offset parameter
to dma_buf_map_memcpy_to() and update the callers. There are only two
calls to dma_buf_map_memcpy_to() within the kernel. To make it clear
what the offset applies to, I'd call the parameter 'dst_offset'.

> +
> +/**
> + * dma_buf_map_memcpy_from_offset - Memcpy from offset of dma-buf mapping into system memory
> + * @dst: Destination in system memory
> + * @src: The dma-buf mapping structure
> + * @src: The offset from which to copy
> + * @len: The number of byte in src
> + *
> + * Copies data from a dma-buf mapping with an offset. The dest buffer is in
> + * system memory. Depending on the mapping location, the helper picks the
> + * correct method of accessing the memory.
> + */
> +static inline void dma_buf_map_memcpy_from_offset(void *dst, const struct dma_buf_map *src,
> + size_t offset, size_t len)
> +{
> + if (src->is_iomem)
> + memcpy_fromio(dst, src->vaddr_iomem + offset, len);
> + else
> + memcpy(dst, src->vaddr + offset, len);
> +}
> +

With the dma_buf_map_memcpy_to() changes, please just call this function
dma_buf_map_memcpy_from().

> /**
> * dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
> * @dst: The dma-buf mapping structure
> @@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct dma_buf_map *map, size_t incr)
> map->vaddr += incr;
> }
>
> +/**
> + * dma_buf_map_read_field - Read struct member from dma-buf mapping with
> + * arbitrary size and handling un-aligned accesses
> + *
> + * @map__: The dma-buf mapping structure
> + * @type__: The struct to be used containing the field to read
> + * @field__: Member from struct we want to read
> + *
> + * Read a value from dma-buf mapping calculating the offset and size: this assumes
> + * the dma-buf mapping is aligned with a a struct type__. A single u8, u16, u32
> + * or u64 can be read, based on the offset and size of type__.field__.
> + */
> +#define dma_buf_map_read_field(map__, type__, field__) ({ \
> + type__ *t__; \
> + typeof(t__->field__) val__; \
> + dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__, field__), \
> + sizeof(t__->field__)); \
> + val__; \
> +})
> +
> +/**
> + * dma_buf_map_write_field - Write struct member to the dma-buf mapping with
> + * arbitrary size and handling un-aligned accesses
> + *
> + * @map__: The dma-buf mapping structure
> + * @type__: The struct to be used containing the field to write
> + * @field__: Member from struct we want to write
> + * @val__: Value to be written
> + *
> + * Write a value to the dma-buf mapping calculating the offset and size.
> + * A single u8, u16, u32 or u64 can be written based on the offset and size of
> + * type__.field__.
> + */
> +#define dma_buf_map_write_field(map__, type__, field__, val__) ({ \
> + type__ *t__; \
> + typeof(t__->field__) val____ = val__; \
> + dma_buf_map_memcpy_to_offset(map__, offsetof(type__, field__), \
> + &val____, sizeof(t__->field__)); \
> +})

As the original author of this file, I feel like this shouldn't be here.
At least not until we have another driver using that pattern.

Best regards
Thomas

> +
> #endif /* __DMA_BUF_MAP_H__ */

--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev


Attachments:
OpenPGP_signature (855.00 B)
OpenPGP digital signature

2022-01-28 09:39:50

by Lucas De Marchi

[permalink] [raw]
Subject: Re: [PATCH 01/19] dma-buf-map: Add read/write helpers

On Thu, Jan 27, 2022 at 03:26:43PM +0100, Thomas Zimmermann wrote:
>Hi
>
>Am 26.01.22 um 21:36 schrieb Lucas De Marchi:
>>In certain situations it's useful to be able to read or write to an
>>offset that is calculated by having the memory layout given by a struct
>>declaration. Usually we are going to read/write a u8, u16, u32 or u64.
>>
>>Add a pair of macros dma_buf_map_read_field()/dma_buf_map_write_field()
>>to calculate the offset of a struct member and memcpy the data from/to
>>the dma_buf_map. We could use readb, readw, readl, readq and the write*
>>counterparts, however due to alignment issues this may not work on all
>>architectures. If alignment needs to be checked to call the right
>>function, it's not possible to decide at compile-time which function to
>>call: so just leave the decision to the memcpy function that will do
>>exactly that on IO memory or dereference the pointer.
>>
>>Cc: Sumit Semwal <[email protected]>
>>Cc: Christian K?nig <[email protected]>
>>Cc: [email protected]
>>Cc: [email protected]
>>Cc: [email protected]
>>Cc: [email protected]
>>Signed-off-by: Lucas De Marchi <[email protected]>
>>---
>> include/linux/dma-buf-map.h | 81 +++++++++++++++++++++++++++++++++++++
>> 1 file changed, 81 insertions(+)
>>
>>diff --git a/include/linux/dma-buf-map.h b/include/linux/dma-buf-map.h
>>index 19fa0b5ae5ec..65e927d9ce33 100644
>>--- a/include/linux/dma-buf-map.h
>>+++ b/include/linux/dma-buf-map.h
>>@@ -6,6 +6,7 @@
>> #ifndef __DMA_BUF_MAP_H__
>> #define __DMA_BUF_MAP_H__
>>+#include <linux/kernel.h>
>> #include <linux/io.h>
>> #include <linux/string.h>
>>@@ -229,6 +230,46 @@ static inline void dma_buf_map_clear(struct dma_buf_map *map)
>> }
>> }
>>+/**
>>+ * dma_buf_map_memcpy_to_offset - Memcpy into offset of dma-buf mapping
>>+ * @dst: The dma-buf mapping structure
>>+ * @offset: The offset from which to copy
>>+ * @src: The source buffer
>>+ * @len: The number of byte in src
>>+ *
>>+ * Copies data into a dma-buf mapping with an offset. The source buffer is in
>>+ * system memory. Depending on the buffer's location, the helper picks the
>>+ * correct method of accessing the memory.
>>+ */
>>+static inline void dma_buf_map_memcpy_to_offset(struct dma_buf_map *dst, size_t offset,
>>+ const void *src, size_t len)
>>+{
>>+ if (dst->is_iomem)
>>+ memcpy_toio(dst->vaddr_iomem + offset, src, len);
>>+ else
>>+ memcpy(dst->vaddr + offset, src, len);
>>+}
>
>Please don't add a new function. Rather please add the offset
>parameter to dma_buf_map_memcpy_to() and update the callers. There are
>only two calls to dma_buf_map_memcpy_to() within the kernel. To make
>it clear what the offset applies to, I'd call the parameter
>'dst_offset'.
>
>>+
>>+/**
>>+ * dma_buf_map_memcpy_from_offset - Memcpy from offset of dma-buf mapping into system memory
>>+ * @dst: Destination in system memory
>>+ * @src: The dma-buf mapping structure
>>+ * @src: The offset from which to copy
>>+ * @len: The number of byte in src
>>+ *
>>+ * Copies data from a dma-buf mapping with an offset. The dest buffer is in
>>+ * system memory. Depending on the mapping location, the helper picks the
>>+ * correct method of accessing the memory.
>>+ */
>>+static inline void dma_buf_map_memcpy_from_offset(void *dst, const struct dma_buf_map *src,
>>+ size_t offset, size_t len)
>>+{
>>+ if (src->is_iomem)
>>+ memcpy_fromio(dst, src->vaddr_iomem + offset, len);
>>+ else
>>+ memcpy(dst, src->vaddr + offset, len);
>>+}
>>+
>
>With the dma_buf_map_memcpy_to() changes, please just call this
>function dma_buf_map_memcpy_from().
>
>> /**
>> * dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
>> * @dst: The dma-buf mapping structure
>>@@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct dma_buf_map *map, size_t incr)
>> map->vaddr += incr;
>> }
>>+/**
>>+ * dma_buf_map_read_field - Read struct member from dma-buf mapping with
>>+ * arbitrary size and handling un-aligned accesses
>>+ *
>>+ * @map__: The dma-buf mapping structure
>>+ * @type__: The struct to be used containing the field to read
>>+ * @field__: Member from struct we want to read
>>+ *
>>+ * Read a value from dma-buf mapping calculating the offset and size: this assumes
>>+ * the dma-buf mapping is aligned with a a struct type__. A single u8, u16, u32
>>+ * or u64 can be read, based on the offset and size of type__.field__.
>>+ */
>>+#define dma_buf_map_read_field(map__, type__, field__) ({ \
>>+ type__ *t__; \
>>+ typeof(t__->field__) val__; \
>>+ dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__, field__), \
>>+ sizeof(t__->field__)); \
>>+ val__; \
>>+})
>>+
>>+/**
>>+ * dma_buf_map_write_field - Write struct member to the dma-buf mapping with
>>+ * arbitrary size and handling un-aligned accesses
>>+ *
>>+ * @map__: The dma-buf mapping structure
>>+ * @type__: The struct to be used containing the field to write
>>+ * @field__: Member from struct we want to write
>>+ * @val__: Value to be written
>>+ *
>>+ * Write a value to the dma-buf mapping calculating the offset and size.
>>+ * A single u8, u16, u32 or u64 can be written based on the offset and size of
>>+ * type__.field__.
>>+ */
>>+#define dma_buf_map_write_field(map__, type__, field__, val__) ({ \
>>+ type__ *t__; \
>>+ typeof(t__->field__) val____ = val__; \
>>+ dma_buf_map_memcpy_to_offset(map__, offsetof(type__, field__), \
>>+ &val____, sizeof(t__->field__)); \
>>+})
>
>As the original author of this file, I feel like this shouldn't be
>here. At least not until we have another driver using that pattern.

Let me try to clear out the confusion. Then maybe I can extend
the documentation of this function in v2 if I'm able to convince this is
useful here.

This is not about importer/exporter, having this to work cross-driver.
This is about using dma_buf_map (which we are talking about on renaming
to iosys_map or something else) for inner driver
allocations/abstractions. The abstraction added by iosys_map helps on
sharing the same functions we had before. And this macro here is very
useful when the buffer is described by a struct layout. Example:

struct bla {
struct inner inner1;
struct inner inner2;
u32 x, y ,z;
};

Functions that would previously do:

struct bla *bla = ...;

bla->x = 100;
bla->y = 200;
bla->inner1.inner_inner_field = 30;

Can do the below, having the system/IO memory abstracted away
(calling it iosys_map here instead of dma_buf_map, hopeful it helps):

struct iosys_map *map = ...;

iosys_map_write_field(map, struct bla, x, 100);
iosys_map_write_field(map, struct bla, y, 200);
iosys_map_write_field(map, struct bla,
inner1.inner_inner_field, 30);

When we are using mostly the same map, the individual drivers can add
quick helpers on top. See the ads_blob_write() added in this series,
which guarantees the map it's working on is always the guc->ads_map,
while reducing verbosity to use the API. From patch
"drm/i915/guc: Add read/write helpers for ADS blob":

#define ads_blob_read(guc_, field_) \
dma_buf_map_read_field(&(guc_)->ads_map, struct __guc_ads_blob, \
field_)

#define ads_blob_write(guc_, field_, val_) \
dma_buf_map_write_field(&(guc_)->ads_map, struct __guc_ads_blob,\
field_, val_)

So in intel_guc_ads, we can have a lot of:

- bla->x = 100;
+ ads_blob_write(guc, x, 10);

thanks
Lucas De Marchi

2022-01-30 14:46:59

by Thomas Zimmermann

[permalink] [raw]
Subject: Re: [PATCH 01/19] dma-buf-map: Add read/write helpers

Hi

Am 27.01.22 um 17:34 schrieb Lucas De Marchi:
> On Thu, Jan 27, 2022 at 03:26:43PM +0100, Thomas Zimmermann wrote:
>> Hi
>>
>> Am 26.01.22 um 21:36 schrieb Lucas De Marchi:
>>> In certain situations it's useful to be able to read or write to an
>>> offset that is calculated by having the memory layout given by a struct
>>> declaration. Usually we are going to read/write a u8, u16, u32 or u64.
>>>
>>> Add a pair of macros dma_buf_map_read_field()/dma_buf_map_write_field()
>>> to calculate the offset of a struct member and memcpy the data from/to
>>> the dma_buf_map. We could use readb, readw, readl, readq and the write*
>>> counterparts, however due to alignment issues this may not work on all
>>> architectures. If alignment needs to be checked to call the right
>>> function, it's not possible to decide at compile-time which function to
>>> call: so just leave the decision to the memcpy function that will do
>>> exactly that on IO memory or dereference the pointer.
>>>
>>> Cc: Sumit Semwal <[email protected]>
>>> Cc: Christian König <[email protected]>
>>> Cc: [email protected]
>>> Cc: [email protected]
>>> Cc: [email protected]
>>> Cc: [email protected]
>>> Signed-off-by: Lucas De Marchi <[email protected]>
>>> ---
>>>  include/linux/dma-buf-map.h | 81 +++++++++++++++++++++++++++++++++++++
>>>  1 file changed, 81 insertions(+)
>>>
>>> diff --git a/include/linux/dma-buf-map.h b/include/linux/dma-buf-map.h
>>> index 19fa0b5ae5ec..65e927d9ce33 100644
>>> --- a/include/linux/dma-buf-map.h
>>> +++ b/include/linux/dma-buf-map.h
>>> @@ -6,6 +6,7 @@
>>>  #ifndef __DMA_BUF_MAP_H__
>>>  #define __DMA_BUF_MAP_H__
>>> +#include <linux/kernel.h>
>>>  #include <linux/io.h>
>>>  #include <linux/string.h>
>>> @@ -229,6 +230,46 @@ static inline void dma_buf_map_clear(struct
>>> dma_buf_map *map)
>>>      }
>>>  }
>>> +/**
>>> + * dma_buf_map_memcpy_to_offset - Memcpy into offset of dma-buf mapping
>>> + * @dst:    The dma-buf mapping structure
>>> + * @offset:    The offset from which to copy
>>> + * @src:    The source buffer
>>> + * @len:    The number of byte in src
>>> + *
>>> + * Copies data into a dma-buf mapping with an offset. The source
>>> buffer is in
>>> + * system memory. Depending on the buffer's location, the helper
>>> picks the
>>> + * correct method of accessing the memory.
>>> + */
>>> +static inline void dma_buf_map_memcpy_to_offset(struct dma_buf_map
>>> *dst, size_t offset,
>>> +                        const void *src, size_t len)
>>> +{
>>> +    if (dst->is_iomem)
>>> +        memcpy_toio(dst->vaddr_iomem + offset, src, len);
>>> +    else
>>> +        memcpy(dst->vaddr + offset, src, len);
>>> +}
>>
>> Please don't add a new function. Rather please add the offset
>> parameter to dma_buf_map_memcpy_to() and update the callers. There are
>> only two calls to dma_buf_map_memcpy_to() within the kernel. To make
>> it clear what the offset applies to, I'd call the parameter 'dst_offset'.
>>
>>> +
>>> +/**
>>> + * dma_buf_map_memcpy_from_offset - Memcpy from offset of dma-buf
>>> mapping into system memory
>>> + * @dst:    Destination in system memory
>>> + * @src:    The dma-buf mapping structure
>>> + * @src:    The offset from which to copy
>>> + * @len:    The number of byte in src
>>> + *
>>> + * Copies data from a dma-buf mapping with an offset. The dest
>>> buffer is in
>>> + * system memory. Depending on the mapping location, the helper
>>> picks the
>>> + * correct method of accessing the memory.
>>> + */
>>> +static inline void dma_buf_map_memcpy_from_offset(void *dst, const
>>> struct dma_buf_map *src,
>>> +                          size_t offset, size_t len)
>>> +{
>>> +    if (src->is_iomem)
>>> +        memcpy_fromio(dst, src->vaddr_iomem + offset, len);
>>> +    else
>>> +        memcpy(dst, src->vaddr + offset, len);
>>> +}
>>> +
>>
>> With the dma_buf_map_memcpy_to() changes, please just call this
>> function dma_buf_map_memcpy_from().
>>
>>>  /**
>>>   * dma_buf_map_memcpy_to - Memcpy into dma-buf mapping
>>>   * @dst:    The dma-buf mapping structure
>>> @@ -263,4 +304,44 @@ static inline void dma_buf_map_incr(struct
>>> dma_buf_map *map, size_t incr)
>>>          map->vaddr += incr;
>>>  }
>>> +/**
>>> + * dma_buf_map_read_field - Read struct member from dma-buf mapping
>>> with
>>> + * arbitrary size and handling un-aligned accesses
>>> + *
>>> + * @map__:    The dma-buf mapping structure
>>> + * @type__:    The struct to be used containing the field to read
>>> + * @field__:    Member from struct we want to read
>>> + *
>>> + * Read a value from dma-buf mapping calculating the offset and
>>> size: this assumes
>>> + * the dma-buf mapping is aligned with a a struct type__. A single
>>> u8, u16, u32
>>> + * or u64 can be read, based on the offset and size of type__.field__.
>>> + */
>>> +#define dma_buf_map_read_field(map__, type__, field__)
>>> ({                \
>>> +    type__ *t__;                                    \
>>> +    typeof(t__->field__) val__;                            \
>>> +    dma_buf_map_memcpy_from_offset(&val__, map__, offsetof(type__,
>>> field__),    \
>>> +                       sizeof(t__->field__));                \
>>> +    val__;                                        \
>>> +})
>>> +
>>> +/**
>>> + * dma_buf_map_write_field - Write struct member to the dma-buf
>>> mapping with
>>> + * arbitrary size and handling un-aligned accesses
>>> + *
>>> + * @map__:    The dma-buf mapping structure
>>> + * @type__:    The struct to be used containing the field to write
>>> + * @field__:    Member from struct we want to write
>>> + * @val__:    Value to be written
>>> + *
>>> + * Write a value to the dma-buf mapping calculating the offset and
>>> size.
>>> + * A single u8, u16, u32 or u64 can be written based on the offset
>>> and size of
>>> + * type__.field__.
>>> + */
>>> +#define dma_buf_map_write_field(map__, type__, field__, val__)
>>> ({            \
>>> +    type__ *t__;                                    \
>>> +    typeof(t__->field__) val____ = val__;                        \
>>> +    dma_buf_map_memcpy_to_offset(map__, offsetof(type__,
>>> field__),            \
>>> +                     &val____, sizeof(t__->field__));            \
>>> +})
>>
>> As the original author of this file, I feel like this shouldn't be
>> here. At least not until we have another driver using that pattern.
>
> Let me try to clear out the confusion. Then maybe I can extend
> the documentation of this function in v2 if I'm able to convince this is
> useful here.
>
> This is not about importer/exporter, having this to work cross-driver.
> This is about using dma_buf_map (which we are talking about on renaming
> to iosys_map or something else) for inner driver
> allocations/abstractions. The abstraction added by iosys_map helps on
> sharing the same functions we had before.  And this macro here is very
> useful when the buffer is described by a struct layout. Example:
>
>     struct bla {
>         struct inner inner1;
>         struct inner inner2;
>         u32 x, y ,z;
>     };
>
> Functions that would previously do:
>
>     struct bla *bla = ...;
>
>     bla->x = 100;
>     bla->y = 200;
>     bla->inner1.inner_inner_field = 30;
>
> Can do the below, having the system/IO memory abstracted away
> (calling it iosys_map here instead of dma_buf_map, hopeful it helps):
>
>     struct iosys_map *map = ...;

Please don't start renaming anything here. If we want to do this, let's
have a separate mail thread for coloring the bike shed.

>
>     iosys_map_write_field(map, struct bla, x, 100);
>     iosys_map_write_field(map, struct bla, y, 200);
>     iosys_map_write_field(map, struct bla,
>                   inner1.inner_inner_field, 30);

I don't have strong feelings about these macros. They just seemed not
needed in general. But I we want to add them here, I 'd like to propose
a few small changes.

Again, please add an offset parameter for the map's pointer.

Then I'd call them either dma_buf_map_rd/dma_buf_map_wr for read/write
OR dma_buf_map_ld/dma_buf_map_st for load/store. They should take a C
type. Something like this

dma_buf_map_wr(map, offset, int32, 0x01234);
val = dam_buf_map_rd(map, offset, int32);

Hopefully, that's flexible enough for all users. On top of that, you can
build additional helpers like dma_buf_map_rd_field() and
dma_buf_map_wr_field().

Ok?

Best regards
Thomas

>
> When we are using mostly the same map, the individual drivers can add
> quick helpers on top. See the ads_blob_write() added in this series,
> which guarantees the map it's working on is always the guc->ads_map,
> while reducing verbosity to use the API. From patch
> "drm/i915/guc: Add read/write helpers for ADS blob":
>
> #define ads_blob_read(guc_, field_)                                    \
>        dma_buf_map_read_field(&(guc_)->ads_map, struct __guc_ads_blob, \
>                               field_)
>
> #define ads_blob_write(guc_, field_, val_)                             \
>        dma_buf_map_write_field(&(guc_)->ads_map, struct __guc_ads_blob,\
>                                field_, val_)
>
> So in intel_guc_ads, we can have a lot of:
>
> -    bla->x = 100;
> +    ads_blob_write(guc, x, 10);
>
> thanks
> Lucas De Marchi

--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev


Attachments:
OpenPGP_signature (855.00 B)
OpenPGP digital signature