diff mbox series

[v2,02/18] iosys-map: Add a few more helpers

Message ID 20220208104524.2516209-3-lucas.demarchi@intel.com (mailing list archive)
State New, archived
Headers show
Series drm/i915/guc: Refactor ADS access to use iosys_map | expand

Commit Message

Lucas De Marchi Feb. 8, 2022, 10:45 a.m. UTC
First the simplest ones:

	- iosys_map_memset(): when abstracting system and I/O memory,
	  just like the memcpy() use case, memset() also has dedicated
	  functions to be called for using IO memory.
	- iosys_map_memcpy_from(): we may need to copy data from I/O
	  memory, not only to.

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.

As a pre-requisite for the implementation, add iosys_map_memcpy_from()
to be the equivalent of iosys_map_memcpy_to(), but in the other
direction. Then add 2 pairs of macros:

	- iosys_map_rd() / iosys_map_wr()
	- iosys_map_rd_field() / iosys_map_wr_field()

The first pair takes the C-type and offset to read/write. The second
pair uses a struct describing the layout of the mapping in order to
calculate the offset and size being read/written.

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.

Finally, in order to use the above macros with a map derived from
another, add another initializer: IOSYS_MAP_INIT_OFFSET().

v2:
  - Rework IOSYS_MAP_INIT_OFFSET() so it doesn't rely on aliasing rules
    within the union
  - Add offset to both iosys_map_rd_field() and iosys_map_wr_field() to
    allow the struct itself to be at an offset from the mapping
  - Add documentation to iosys_map_rd_field() with example and expected
    memory layout

Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Christian König <christian.koenig@amd.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
---
 include/linux/iosys-map.h | 202 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 202 insertions(+)

Comments

Matt Atwood Feb. 8, 2022, 10:53 p.m. UTC | #1
On Tue, Feb 08, 2022 at 02:45:08AM -0800, Lucas De Marchi wrote:
> First the simplest ones:
> 
> 	- iosys_map_memset(): when abstracting system and I/O memory,
> 	  just like the memcpy() use case, memset() also has dedicated
> 	  functions to be called for using IO memory.
> 	- iosys_map_memcpy_from(): we may need to copy data from I/O
> 	  memory, not only to.
> 
> 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.
> 
> As a pre-requisite for the implementation, add iosys_map_memcpy_from()
> to be the equivalent of iosys_map_memcpy_to(), but in the other
> direction. Then add 2 pairs of macros:
> 
> 	- iosys_map_rd() / iosys_map_wr()
> 	- iosys_map_rd_field() / iosys_map_wr_field()
> 
> The first pair takes the C-type and offset to read/write. The second
> pair uses a struct describing the layout of the mapping in order to
> calculate the offset and size being read/written.
> 
> 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.
> 
> Finally, in order to use the above macros with a map derived from
> another, add another initializer: IOSYS_MAP_INIT_OFFSET().
> 
> v2:
>   - Rework IOSYS_MAP_INIT_OFFSET() so it doesn't rely on aliasing rules
>     within the union
>   - Add offset to both iosys_map_rd_field() and iosys_map_wr_field() to
>     allow the struct itself to be at an offset from the mapping
>   - Add documentation to iosys_map_rd_field() with example and expected
>     memory layout
> 
> Cc: Sumit Semwal <sumit.semwal@linaro.org>
> Cc: Christian König <christian.koenig@amd.com>
> Cc: Thomas Zimmermann <tzimmermann@suse.de>
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
> Cc: dri-devel@lists.freedesktop.org
> Cc: linux-kernel@vger.kernel.org
Reviewed-by: Matt Atwood <matthew.s.atwood@intel.com>
> Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
> ---
>  include/linux/iosys-map.h | 202 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 202 insertions(+)
> 
> diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h
> index edd730b1e899..c6b223534b21 100644
> --- a/include/linux/iosys-map.h
> +++ b/include/linux/iosys-map.h
> @@ -6,6 +6,7 @@
>  #ifndef __IOSYS_MAP_H__
>  #define __IOSYS_MAP_H__
>  
> +#include <linux/kernel.h>
>  #include <linux/io.h>
>  #include <linux/string.h>
>  
> @@ -120,6 +121,45 @@ struct iosys_map {
>  		.is_iomem = false,	\
>  	}
>  
> +/**
> + * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map
> + * @map_:	The dma-buf mapping structure to copy from
> + * @offset_:	Offset to add to the other mapping
> + *
> + * Initializes a new iosys_map struct based on another passed as argument. It
> + * does a shallow copy of the struct so it's possible to update the back storage
> + * without changing where the original map points to. It is the equivalent of
> + * doing:
> + *
> + * .. code-block:: c
> + *
> + *	iosys_map map = other_map;
> + *	iosys_map_incr(&map, &offset);
> + *
> + * Example usage:
> + *
> + * .. code-block:: c
> + *
> + *	void foo(struct device *dev, struct iosys_map *base_map)
> + *	{
> + *		...
> + *		struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET);
> + *		...
> + *	}
> + *
> + * The advantage of using the initializer over just increasing the offset with
> + * iosys_map_incr() like above is that the new map will always point to the
> + * right place of the buffer during its scope. It reduces the risk of updating
> + * the wrong part of the buffer and having no compiler warning about that. If
> + * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn
> + * about the use of uninitialized variable.
> + */
> +#define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({				\
> +	struct iosys_map copy = *map_;					\
> +	iosys_map_incr(&copy, offset_);					\
> +	copy;								\
> +})
> +
>  /**
>   * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory
>   * @map:	The iosys_map structure
> @@ -239,6 +279,26 @@ static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
>  		memcpy(dst->vaddr + dst_offset, src, len);
>  }
>  
> +/**
> + * iosys_map_memcpy_from - Memcpy from iosys_map into system memory
> + * @dst:	Destination in system memory
> + * @src:	The iosys_map structure
> + * @src_offset:	The offset from which to copy
> + * @len:	The number of byte in src
> + *
> + * Copies data from a iosys_map 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 iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
> +					 size_t src_offset, size_t len)
> +{
> +	if (src->is_iomem)
> +		memcpy_fromio(dst, src->vaddr_iomem + src_offset, len);
> +	else
> +		memcpy(dst, src->vaddr + src_offset, len);
> +}
> +
>  /**
>   * iosys_map_incr - Increments the address stored in a iosys mapping
>   * @map:	The iosys_map structure
> @@ -255,4 +315,146 @@ static inline void iosys_map_incr(struct iosys_map *map, size_t incr)
>  		map->vaddr += incr;
>  }
>  
> +/**
> + * iosys_map_memset - Memset iosys_map
> + * @dst:	The iosys_map structure
> + * @offset:	Offset from dst where to start setting value
> + * @value:	The value to set
> + * @len:	The number of bytes to set in dst
> + *
> + * Set value in iosys_map. Depending on the buffer's location, the helper
> + * picks the correct method of accessing the memory.
> + */
> +static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
> +				    int value, size_t len)
> +{
> +	if (dst->is_iomem)
> +		memset_io(dst->vaddr_iomem + offset, value, len);
> +	else
> +		memset(dst->vaddr + offset, value, len);
> +}
> +
> +/**
> + * iosys_map_rd - Read a C-type value from the iosys_map
> + *
> + * @map__:	The iosys_map structure
> + * @offset__:	The offset from which to read
> + * @type__:	Type of the value being read
> + *
> + * Read a C type value from iosys_map, handling possible un-aligned accesses to
> + * the mapping.
> + *
> + * Returns:
> + * The value read from the mapping.
> + */
> +#define iosys_map_rd(map__, offset__, type__) ({			\
> +	type__ val;							\
> +	iosys_map_memcpy_from(&val, map__, offset__, sizeof(val));	\
> +	val;								\
> +})
> +
> +/**
> + * iosys_map_wr - Write a C-type value to the iosys_map
> + *
> + * @map__:	The iosys_map structure
> + * @offset__:	The offset from the mapping to write to
> + * @type__:	Type of the value being written
> + * @val__:	Value to write
> + *
> + * Write a C-type value to the iosys_map, handling possible un-aligned accesses
> + * to the mapping.
> + */
> +#define iosys_map_wr(map__, offset__, type__, val__) ({			\
> +	type__ val = (val__);						\
> +	iosys_map_memcpy_to(map__, offset__, &val, sizeof(val));	\
> +})
> +
> +/**
> + * iosys_map_rd_field - Read a member from a struct in the iosys_map
> + *
> + * @map__:		The iosys_map structure
> + * @struct_offset__:	Offset from the beggining of the map, where the struct
> + *			is located
> + * @struct_type__:	The struct describing the layout of the mapping
> + * @field__:		Member of the struct to read
> + *
> + * Read a value from iosys_map considering its layout is described by a C struct
> + * starting at @struct_offset__. The field offset and size is calculated and its
> + * value read handling possible un-aligned memory accesses. For example: suppose
> + * there is a @struct foo defined as below and the value ``foo.field2.inner2``
> + * needs to be read from the iosys_map:
> + *
> + * .. code-block:: c
> + *
> + *	struct foo {
> + *		int field1;
> + *		struct {
> + *			int inner1;
> + *			int inner2;
> + *		} field2;
> + *		int field3;
> + *	} __packed;
> + *
> + * This is the expected memory layout of a buffer using iosys_map_rd_field():
> + *
> + * +------------------------------+--------------------------+
> + * | Address                      | Content                  |
> + * +==============================+==========================+
> + * | buffer + 0000                | start of mmapped buffer  |
> + * |                              | pointed by iosys_map     |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + ``struct_offset__`` | start of ``struct foo``  |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + wwww                | ``foo.field2.inner2``    |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + yyyy                | end of ``struct foo``    |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + zzzz                | end of mmaped buffer     |
> + * +------------------------------+--------------------------+
> + *
> + * Values automatically calculated by this macro or not needed are denoted by
> + * wwww, yyyy and zzzz. This is the code to read that value:
> + *
> + * .. code-block:: c
> + *
> + *	x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2);
> + *
> + * Returns:
> + * The value read from the mapping.
> + */
> +#define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({	\
> +	struct_type__ *s;							\
> +	iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__),	\
> +		     typeof(s->field__));					\
> +})
> +
> +/**
> + * iosys_map_wr_field - Write to a member of a struct in the iosys_map
> + *
> + * @map__:		The iosys_map structure
> + * @struct_offset__:	Offset from the beggining of the map, where the struct
> + *			is located
> + * @struct_type__:	The struct describing the layout of the mapping
> + * @field__:		Member of the struct to read
> + * @val__:		Value to write
> + *
> + * Write a value to the iosys_map considering its layout is described by a C struct
> + * starting at @struct_offset__. The field offset and size is calculated and the
> + * @val__ is written handling possible un-aligned memory accesses. Refer to
> + * iosys_map_rd_field() for expected usage and memory layout.
> + */
> +#define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({	\
> +	struct_type__ *s;								\
> +	iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__),		\
> +		     typeof(s->field__), val__);					\
> +})
> +
>  #endif /* __IOSYS_MAP_H__ */
> -- 
> 2.35.1
>
Mauro Carvalho Chehab Feb. 9, 2022, 6:14 a.m. UTC | #2
Em Tue,  8 Feb 2022 02:45:08 -0800
Lucas De Marchi <lucas.demarchi@intel.com> escreveu:

> First the simplest ones:
> 
> 	- iosys_map_memset(): when abstracting system and I/O memory,
> 	  just like the memcpy() use case, memset() also has dedicated
> 	  functions to be called for using IO memory.
> 	- iosys_map_memcpy_from(): we may need to copy data from I/O
> 	  memory, not only to.
> 
> 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.
> 
> As a pre-requisite for the implementation, add iosys_map_memcpy_from()
> to be the equivalent of iosys_map_memcpy_to(), but in the other
> direction. Then add 2 pairs of macros:
> 
> 	- iosys_map_rd() / iosys_map_wr()
> 	- iosys_map_rd_field() / iosys_map_wr_field()
> 
> The first pair takes the C-type and offset to read/write. The second
> pair uses a struct describing the layout of the mapping in order to
> calculate the offset and size being read/written.
> 
> 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.
> 
> Finally, in order to use the above macros with a map derived from
> another, add another initializer: IOSYS_MAP_INIT_OFFSET().
> 
> v2:
>   - Rework IOSYS_MAP_INIT_OFFSET() so it doesn't rely on aliasing rules
>     within the union
>   - Add offset to both iosys_map_rd_field() and iosys_map_wr_field() to
>     allow the struct itself to be at an offset from the mapping
>   - Add documentation to iosys_map_rd_field() with example and expected
>     memory layout
> 
> Cc: Sumit Semwal <sumit.semwal@linaro.org>
> Cc: Christian König <christian.koenig@amd.com>
> Cc: Thomas Zimmermann <tzimmermann@suse.de>
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
> Cc: dri-devel@lists.freedesktop.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>

LGTM.

Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org>

Regards,
Mauro

> ---
>  include/linux/iosys-map.h | 202 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 202 insertions(+)
> 
> diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h
> index edd730b1e899..c6b223534b21 100644
> --- a/include/linux/iosys-map.h
> +++ b/include/linux/iosys-map.h
> @@ -6,6 +6,7 @@
>  #ifndef __IOSYS_MAP_H__
>  #define __IOSYS_MAP_H__
>  
> +#include <linux/kernel.h>
>  #include <linux/io.h>
>  #include <linux/string.h>
>  
> @@ -120,6 +121,45 @@ struct iosys_map {
>  		.is_iomem = false,	\
>  	}
>  
> +/**
> + * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map
> + * @map_:	The dma-buf mapping structure to copy from
> + * @offset_:	Offset to add to the other mapping
> + *
> + * Initializes a new iosys_map struct based on another passed as argument. It
> + * does a shallow copy of the struct so it's possible to update the back storage
> + * without changing where the original map points to. It is the equivalent of
> + * doing:
> + *
> + * .. code-block:: c
> + *
> + *	iosys_map map = other_map;
> + *	iosys_map_incr(&map, &offset);
> + *
> + * Example usage:
> + *
> + * .. code-block:: c
> + *
> + *	void foo(struct device *dev, struct iosys_map *base_map)
> + *	{
> + *		...
> + *		struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET);
> + *		...
> + *	}
> + *
> + * The advantage of using the initializer over just increasing the offset with
> + * iosys_map_incr() like above is that the new map will always point to the
> + * right place of the buffer during its scope. It reduces the risk of updating
> + * the wrong part of the buffer and having no compiler warning about that. If
> + * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn
> + * about the use of uninitialized variable.
> + */
> +#define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({				\
> +	struct iosys_map copy = *map_;					\
> +	iosys_map_incr(&copy, offset_);					\
> +	copy;								\
> +})
> +
>  /**
>   * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory
>   * @map:	The iosys_map structure
> @@ -239,6 +279,26 @@ static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
>  		memcpy(dst->vaddr + dst_offset, src, len);
>  }
>  
> +/**
> + * iosys_map_memcpy_from - Memcpy from iosys_map into system memory
> + * @dst:	Destination in system memory
> + * @src:	The iosys_map structure
> + * @src_offset:	The offset from which to copy
> + * @len:	The number of byte in src
> + *
> + * Copies data from a iosys_map 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 iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
> +					 size_t src_offset, size_t len)
> +{
> +	if (src->is_iomem)
> +		memcpy_fromio(dst, src->vaddr_iomem + src_offset, len);
> +	else
> +		memcpy(dst, src->vaddr + src_offset, len);
> +}
> +
>  /**
>   * iosys_map_incr - Increments the address stored in a iosys mapping
>   * @map:	The iosys_map structure
> @@ -255,4 +315,146 @@ static inline void iosys_map_incr(struct iosys_map *map, size_t incr)
>  		map->vaddr += incr;
>  }
>  
> +/**
> + * iosys_map_memset - Memset iosys_map
> + * @dst:	The iosys_map structure
> + * @offset:	Offset from dst where to start setting value
> + * @value:	The value to set
> + * @len:	The number of bytes to set in dst
> + *
> + * Set value in iosys_map. Depending on the buffer's location, the helper
> + * picks the correct method of accessing the memory.
> + */
> +static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
> +				    int value, size_t len)
> +{
> +	if (dst->is_iomem)
> +		memset_io(dst->vaddr_iomem + offset, value, len);
> +	else
> +		memset(dst->vaddr + offset, value, len);
> +}
> +
> +/**
> + * iosys_map_rd - Read a C-type value from the iosys_map
> + *
> + * @map__:	The iosys_map structure
> + * @offset__:	The offset from which to read
> + * @type__:	Type of the value being read
> + *
> + * Read a C type value from iosys_map, handling possible un-aligned accesses to
> + * the mapping.
> + *
> + * Returns:
> + * The value read from the mapping.
> + */
> +#define iosys_map_rd(map__, offset__, type__) ({			\
> +	type__ val;							\
> +	iosys_map_memcpy_from(&val, map__, offset__, sizeof(val));	\
> +	val;								\
> +})
> +
> +/**
> + * iosys_map_wr - Write a C-type value to the iosys_map
> + *
> + * @map__:	The iosys_map structure
> + * @offset__:	The offset from the mapping to write to
> + * @type__:	Type of the value being written
> + * @val__:	Value to write
> + *
> + * Write a C-type value to the iosys_map, handling possible un-aligned accesses
> + * to the mapping.
> + */
> +#define iosys_map_wr(map__, offset__, type__, val__) ({			\
> +	type__ val = (val__);						\
> +	iosys_map_memcpy_to(map__, offset__, &val, sizeof(val));	\
> +})
> +
> +/**
> + * iosys_map_rd_field - Read a member from a struct in the iosys_map
> + *
> + * @map__:		The iosys_map structure
> + * @struct_offset__:	Offset from the beggining of the map, where the struct
> + *			is located
> + * @struct_type__:	The struct describing the layout of the mapping
> + * @field__:		Member of the struct to read
> + *
> + * Read a value from iosys_map considering its layout is described by a C struct
> + * starting at @struct_offset__. The field offset and size is calculated and its
> + * value read handling possible un-aligned memory accesses. For example: suppose
> + * there is a @struct foo defined as below and the value ``foo.field2.inner2``
> + * needs to be read from the iosys_map:
> + *
> + * .. code-block:: c
> + *
> + *	struct foo {
> + *		int field1;
> + *		struct {
> + *			int inner1;
> + *			int inner2;
> + *		} field2;
> + *		int field3;
> + *	} __packed;
> + *
> + * This is the expected memory layout of a buffer using iosys_map_rd_field():
> + *
> + * +------------------------------+--------------------------+
> + * | Address                      | Content                  |
> + * +==============================+==========================+
> + * | buffer + 0000                | start of mmapped buffer  |
> + * |                              | pointed by iosys_map     |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + ``struct_offset__`` | start of ``struct foo``  |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + wwww                | ``foo.field2.inner2``    |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + yyyy                | end of ``struct foo``    |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + zzzz                | end of mmaped buffer     |
> + * +------------------------------+--------------------------+
> + *
> + * Values automatically calculated by this macro or not needed are denoted by
> + * wwww, yyyy and zzzz. This is the code to read that value:
> + *
> + * .. code-block:: c
> + *
> + *	x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2);
> + *
> + * Returns:
> + * The value read from the mapping.
> + */
> +#define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({	\
> +	struct_type__ *s;							\
> +	iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__),	\
> +		     typeof(s->field__));					\
> +})
> +
> +/**
> + * iosys_map_wr_field - Write to a member of a struct in the iosys_map
> + *
> + * @map__:		The iosys_map structure
> + * @struct_offset__:	Offset from the beggining of the map, where the struct
> + *			is located
> + * @struct_type__:	The struct describing the layout of the mapping
> + * @field__:		Member of the struct to read
> + * @val__:		Value to write
> + *
> + * Write a value to the iosys_map considering its layout is described by a C struct
> + * starting at @struct_offset__. The field offset and size is calculated and the
> + * @val__ is written handling possible un-aligned memory accesses. Refer to
> + * iosys_map_rd_field() for expected usage and memory layout.
> + */
> +#define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({	\
> +	struct_type__ *s;								\
> +	iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__),		\
> +		     typeof(s->field__), val__);					\
> +})
> +
>  #endif /* __IOSYS_MAP_H__ */



Thanks,
Mauro
Thomas Zimmermann Feb. 9, 2022, 6:23 a.m. UTC | #3
Hi

Am 08.02.22 um 11:45 schrieb Lucas De Marchi:
> First the simplest ones:
> 
> 	- iosys_map_memset(): when abstracting system and I/O memory,
> 	  just like the memcpy() use case, memset() also has dedicated
> 	  functions to be called for using IO memory.
> 	- iosys_map_memcpy_from(): we may need to copy data from I/O
> 	  memory, not only to.
> 
> 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.
> 
> As a pre-requisite for the implementation, add iosys_map_memcpy_from()
> to be the equivalent of iosys_map_memcpy_to(), but in the other
> direction. Then add 2 pairs of macros:
> 
> 	- iosys_map_rd() / iosys_map_wr()
> 	- iosys_map_rd_field() / iosys_map_wr_field()
> 
> The first pair takes the C-type and offset to read/write. The second
> pair uses a struct describing the layout of the mapping in order to
> calculate the offset and size being read/written.
> 
> 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.
> 
> Finally, in order to use the above macros with a map derived from
> another, add another initializer: IOSYS_MAP_INIT_OFFSET().
> 
> v2:
>    - Rework IOSYS_MAP_INIT_OFFSET() so it doesn't rely on aliasing rules
>      within the union
>    - Add offset to both iosys_map_rd_field() and iosys_map_wr_field() to
>      allow the struct itself to be at an offset from the mapping
>    - Add documentation to iosys_map_rd_field() with example and expected
>      memory layout
> 
> Cc: Sumit Semwal <sumit.semwal@linaro.org>
> Cc: Christian König <christian.koenig@amd.com>
> Cc: Thomas Zimmermann <tzimmermann@suse.de>
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
> Cc: dri-devel@lists.freedesktop.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
> ---
>   include/linux/iosys-map.h | 202 ++++++++++++++++++++++++++++++++++++++
>   1 file changed, 202 insertions(+)
> 
> diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h
> index edd730b1e899..c6b223534b21 100644
> --- a/include/linux/iosys-map.h
> +++ b/include/linux/iosys-map.h
> @@ -6,6 +6,7 @@
>   #ifndef __IOSYS_MAP_H__
>   #define __IOSYS_MAP_H__
>   
> +#include <linux/kernel.h>
>   #include <linux/io.h>
>   #include <linux/string.h>

Alphabetically sorted, please.

What requires kernel.h? Can this be reduced to another include 
statement? Maybe stddef.h for offsetof() ?

Best regards
Thomas

>   
> @@ -120,6 +121,45 @@ struct iosys_map {
>   		.is_iomem = false,	\
>   	}
>   
> +/**
> + * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map
> + * @map_:	The dma-buf mapping structure to copy from
> + * @offset_:	Offset to add to the other mapping
> + *
> + * Initializes a new iosys_map struct based on another passed as argument. It
> + * does a shallow copy of the struct so it's possible to update the back storage
> + * without changing where the original map points to. It is the equivalent of
> + * doing:
> + *
> + * .. code-block:: c
> + *
> + *	iosys_map map = other_map;
> + *	iosys_map_incr(&map, &offset);
> + *
> + * Example usage:
> + *
> + * .. code-block:: c
> + *
> + *	void foo(struct device *dev, struct iosys_map *base_map)
> + *	{
> + *		...
> + *		struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET);
> + *		...
> + *	}
> + *
> + * The advantage of using the initializer over just increasing the offset with
> + * iosys_map_incr() like above is that the new map will always point to the
> + * right place of the buffer during its scope. It reduces the risk of updating
> + * the wrong part of the buffer and having no compiler warning about that. If
> + * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn
> + * about the use of uninitialized variable.
> + */
> +#define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({				\
> +	struct iosys_map copy = *map_;					\
> +	iosys_map_incr(&copy, offset_);					\
> +	copy;								\
> +})
> +
>   /**
>    * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory
>    * @map:	The iosys_map structure
> @@ -239,6 +279,26 @@ static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
>   		memcpy(dst->vaddr + dst_offset, src, len);
>   }
>   
> +/**
> + * iosys_map_memcpy_from - Memcpy from iosys_map into system memory
> + * @dst:	Destination in system memory
> + * @src:	The iosys_map structure
> + * @src_offset:	The offset from which to copy
> + * @len:	The number of byte in src
> + *
> + * Copies data from a iosys_map 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 iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
> +					 size_t src_offset, size_t len)
> +{
> +	if (src->is_iomem)
> +		memcpy_fromio(dst, src->vaddr_iomem + src_offset, len);
> +	else
> +		memcpy(dst, src->vaddr + src_offset, len);
> +}
> +
>   /**
>    * iosys_map_incr - Increments the address stored in a iosys mapping
>    * @map:	The iosys_map structure
> @@ -255,4 +315,146 @@ static inline void iosys_map_incr(struct iosys_map *map, size_t incr)
>   		map->vaddr += incr;
>   }
>   
> +/**
> + * iosys_map_memset - Memset iosys_map
> + * @dst:	The iosys_map structure
> + * @offset:	Offset from dst where to start setting value
> + * @value:	The value to set
> + * @len:	The number of bytes to set in dst
> + *
> + * Set value in iosys_map. Depending on the buffer's location, the helper
> + * picks the correct method of accessing the memory.
> + */
> +static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
> +				    int value, size_t len)
> +{
> +	if (dst->is_iomem)
> +		memset_io(dst->vaddr_iomem + offset, value, len);
> +	else
> +		memset(dst->vaddr + offset, value, len);
> +}
> +
> +/**
> + * iosys_map_rd - Read a C-type value from the iosys_map
> + *
> + * @map__:	The iosys_map structure
> + * @offset__:	The offset from which to read
> + * @type__:	Type of the value being read
> + *
> + * Read a C type value from iosys_map, handling possible un-aligned accesses to
> + * the mapping.
> + *
> + * Returns:
> + * The value read from the mapping.
> + */
> +#define iosys_map_rd(map__, offset__, type__) ({			\
> +	type__ val;							\
> +	iosys_map_memcpy_from(&val, map__, offset__, sizeof(val));	\
> +	val;								\
> +})
> +
> +/**
> + * iosys_map_wr - Write a C-type value to the iosys_map
> + *
> + * @map__:	The iosys_map structure
> + * @offset__:	The offset from the mapping to write to
> + * @type__:	Type of the value being written
> + * @val__:	Value to write
> + *
> + * Write a C-type value to the iosys_map, handling possible un-aligned accesses
> + * to the mapping.
> + */
> +#define iosys_map_wr(map__, offset__, type__, val__) ({			\
> +	type__ val = (val__);						\
> +	iosys_map_memcpy_to(map__, offset__, &val, sizeof(val));	\
> +})
> +
> +/**
> + * iosys_map_rd_field - Read a member from a struct in the iosys_map
> + *
> + * @map__:		The iosys_map structure
> + * @struct_offset__:	Offset from the beggining of the map, where the struct
> + *			is located
> + * @struct_type__:	The struct describing the layout of the mapping
> + * @field__:		Member of the struct to read
> + *
> + * Read a value from iosys_map considering its layout is described by a C struct
> + * starting at @struct_offset__. The field offset and size is calculated and its
> + * value read handling possible un-aligned memory accesses. For example: suppose
> + * there is a @struct foo defined as below and the value ``foo.field2.inner2``
> + * needs to be read from the iosys_map:
> + *
> + * .. code-block:: c
> + *
> + *	struct foo {
> + *		int field1;
> + *		struct {
> + *			int inner1;
> + *			int inner2;
> + *		} field2;
> + *		int field3;
> + *	} __packed;
> + *
> + * This is the expected memory layout of a buffer using iosys_map_rd_field():
> + *
> + * +------------------------------+--------------------------+
> + * | Address                      | Content                  |
> + * +==============================+==========================+
> + * | buffer + 0000                | start of mmapped buffer  |
> + * |                              | pointed by iosys_map     |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + ``struct_offset__`` | start of ``struct foo``  |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + wwww                | ``foo.field2.inner2``    |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + yyyy                | end of ``struct foo``    |
> + * +------------------------------+--------------------------+
> + * | ...                          | ...                      |
> + * +------------------------------+--------------------------+
> + * | buffer + zzzz                | end of mmaped buffer     |
> + * +------------------------------+--------------------------+
> + *
> + * Values automatically calculated by this macro or not needed are denoted by
> + * wwww, yyyy and zzzz. This is the code to read that value:
> + *
> + * .. code-block:: c
> + *
> + *	x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2);
> + *
> + * Returns:
> + * The value read from the mapping.
> + */
> +#define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({	\
> +	struct_type__ *s;							\
> +	iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__),	\
> +		     typeof(s->field__));					\
> +})
> +
> +/**
> + * iosys_map_wr_field - Write to a member of a struct in the iosys_map
> + *
> + * @map__:		The iosys_map structure
> + * @struct_offset__:	Offset from the beggining of the map, where the struct
> + *			is located
> + * @struct_type__:	The struct describing the layout of the mapping
> + * @field__:		Member of the struct to read
> + * @val__:		Value to write
> + *
> + * Write a value to the iosys_map considering its layout is described by a C struct
> + * starting at @struct_offset__. The field offset and size is calculated and the
> + * @val__ is written handling possible un-aligned memory accesses. Refer to
> + * iosys_map_rd_field() for expected usage and memory layout.
> + */
> +#define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({	\
> +	struct_type__ *s;								\
> +	iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__),		\
> +		     typeof(s->field__), val__);					\
> +})
> +
>   #endif /* __IOSYS_MAP_H__ */
Lucas De Marchi Feb. 9, 2022, 6:43 a.m. UTC | #4
On Wed, Feb 09, 2022 at 07:23:04AM +0100, Thomas Zimmermann wrote:
>Hi
>
>Am 08.02.22 um 11:45 schrieb Lucas De Marchi:
>>First the simplest ones:
>>
>>	- iosys_map_memset(): when abstracting system and I/O memory,
>>	  just like the memcpy() use case, memset() also has dedicated
>>	  functions to be called for using IO memory.
>>	- iosys_map_memcpy_from(): we may need to copy data from I/O
>>	  memory, not only to.
>>
>>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.
>>
>>As a pre-requisite for the implementation, add iosys_map_memcpy_from()
>>to be the equivalent of iosys_map_memcpy_to(), but in the other
>>direction. Then add 2 pairs of macros:
>>
>>	- iosys_map_rd() / iosys_map_wr()
>>	- iosys_map_rd_field() / iosys_map_wr_field()
>>
>>The first pair takes the C-type and offset to read/write. The second
>>pair uses a struct describing the layout of the mapping in order to
>>calculate the offset and size being read/written.
>>
>>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.
>>
>>Finally, in order to use the above macros with a map derived from
>>another, add another initializer: IOSYS_MAP_INIT_OFFSET().
>>
>>v2:
>>   - Rework IOSYS_MAP_INIT_OFFSET() so it doesn't rely on aliasing rules
>>     within the union
>>   - Add offset to both iosys_map_rd_field() and iosys_map_wr_field() to
>>     allow the struct itself to be at an offset from the mapping
>>   - Add documentation to iosys_map_rd_field() with example and expected
>>     memory layout
>>
>>Cc: Sumit Semwal <sumit.semwal@linaro.org>
>>Cc: Christian König <christian.koenig@amd.com>
>>Cc: Thomas Zimmermann <tzimmermann@suse.de>
>>Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
>>Cc: dri-devel@lists.freedesktop.org
>>Cc: linux-kernel@vger.kernel.org
>>Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
>>---
>>  include/linux/iosys-map.h | 202 ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 202 insertions(+)
>>
>>diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h
>>index edd730b1e899..c6b223534b21 100644
>>--- a/include/linux/iosys-map.h
>>+++ b/include/linux/iosys-map.h
>>@@ -6,6 +6,7 @@
>>  #ifndef __IOSYS_MAP_H__
>>  #define __IOSYS_MAP_H__
>>+#include <linux/kernel.h>
>>  #include <linux/io.h>
>>  #include <linux/string.h>
>
>Alphabetically sorted, please.
>
>What requires kernel.h? Can this be reduced to another include 
>statement? Maybe stddef.h for offsetof() ?

Humn... I believe it was something in the previous implementation that
is not there anymore. Because this builds fine without the include now
and I don't think it is something being included by the headers already
here.  So this additional include can just be removed.

Lucas De Marchi
diff mbox series

Patch

diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h
index edd730b1e899..c6b223534b21 100644
--- a/include/linux/iosys-map.h
+++ b/include/linux/iosys-map.h
@@ -6,6 +6,7 @@ 
 #ifndef __IOSYS_MAP_H__
 #define __IOSYS_MAP_H__
 
+#include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/string.h>
 
@@ -120,6 +121,45 @@  struct iosys_map {
 		.is_iomem = false,	\
 	}
 
+/**
+ * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map
+ * @map_:	The dma-buf mapping structure to copy from
+ * @offset_:	Offset to add to the other mapping
+ *
+ * Initializes a new iosys_map struct based on another passed as argument. It
+ * does a shallow copy of the struct so it's possible to update the back storage
+ * without changing where the original map points to. It is the equivalent of
+ * doing:
+ *
+ * .. code-block:: c
+ *
+ *	iosys_map map = other_map;
+ *	iosys_map_incr(&map, &offset);
+ *
+ * Example usage:
+ *
+ * .. code-block:: c
+ *
+ *	void foo(struct device *dev, struct iosys_map *base_map)
+ *	{
+ *		...
+ *		struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET);
+ *		...
+ *	}
+ *
+ * The advantage of using the initializer over just increasing the offset with
+ * iosys_map_incr() like above is that the new map will always point to the
+ * right place of the buffer during its scope. It reduces the risk of updating
+ * the wrong part of the buffer and having no compiler warning about that. If
+ * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn
+ * about the use of uninitialized variable.
+ */
+#define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({				\
+	struct iosys_map copy = *map_;					\
+	iosys_map_incr(&copy, offset_);					\
+	copy;								\
+})
+
 /**
  * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory
  * @map:	The iosys_map structure
@@ -239,6 +279,26 @@  static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
 		memcpy(dst->vaddr + dst_offset, src, len);
 }
 
+/**
+ * iosys_map_memcpy_from - Memcpy from iosys_map into system memory
+ * @dst:	Destination in system memory
+ * @src:	The iosys_map structure
+ * @src_offset:	The offset from which to copy
+ * @len:	The number of byte in src
+ *
+ * Copies data from a iosys_map 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 iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
+					 size_t src_offset, size_t len)
+{
+	if (src->is_iomem)
+		memcpy_fromio(dst, src->vaddr_iomem + src_offset, len);
+	else
+		memcpy(dst, src->vaddr + src_offset, len);
+}
+
 /**
  * iosys_map_incr - Increments the address stored in a iosys mapping
  * @map:	The iosys_map structure
@@ -255,4 +315,146 @@  static inline void iosys_map_incr(struct iosys_map *map, size_t incr)
 		map->vaddr += incr;
 }
 
+/**
+ * iosys_map_memset - Memset iosys_map
+ * @dst:	The iosys_map structure
+ * @offset:	Offset from dst where to start setting value
+ * @value:	The value to set
+ * @len:	The number of bytes to set in dst
+ *
+ * Set value in iosys_map. Depending on the buffer's location, the helper
+ * picks the correct method of accessing the memory.
+ */
+static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
+				    int value, size_t len)
+{
+	if (dst->is_iomem)
+		memset_io(dst->vaddr_iomem + offset, value, len);
+	else
+		memset(dst->vaddr + offset, value, len);
+}
+
+/**
+ * iosys_map_rd - Read a C-type value from the iosys_map
+ *
+ * @map__:	The iosys_map structure
+ * @offset__:	The offset from which to read
+ * @type__:	Type of the value being read
+ *
+ * Read a C type value from iosys_map, handling possible un-aligned accesses to
+ * the mapping.
+ *
+ * Returns:
+ * The value read from the mapping.
+ */
+#define iosys_map_rd(map__, offset__, type__) ({			\
+	type__ val;							\
+	iosys_map_memcpy_from(&val, map__, offset__, sizeof(val));	\
+	val;								\
+})
+
+/**
+ * iosys_map_wr - Write a C-type value to the iosys_map
+ *
+ * @map__:	The iosys_map structure
+ * @offset__:	The offset from the mapping to write to
+ * @type__:	Type of the value being written
+ * @val__:	Value to write
+ *
+ * Write a C-type value to the iosys_map, handling possible un-aligned accesses
+ * to the mapping.
+ */
+#define iosys_map_wr(map__, offset__, type__, val__) ({			\
+	type__ val = (val__);						\
+	iosys_map_memcpy_to(map__, offset__, &val, sizeof(val));	\
+})
+
+/**
+ * iosys_map_rd_field - Read a member from a struct in the iosys_map
+ *
+ * @map__:		The iosys_map structure
+ * @struct_offset__:	Offset from the beggining of the map, where the struct
+ *			is located
+ * @struct_type__:	The struct describing the layout of the mapping
+ * @field__:		Member of the struct to read
+ *
+ * Read a value from iosys_map considering its layout is described by a C struct
+ * starting at @struct_offset__. The field offset and size is calculated and its
+ * value read handling possible un-aligned memory accesses. For example: suppose
+ * there is a @struct foo defined as below and the value ``foo.field2.inner2``
+ * needs to be read from the iosys_map:
+ *
+ * .. code-block:: c
+ *
+ *	struct foo {
+ *		int field1;
+ *		struct {
+ *			int inner1;
+ *			int inner2;
+ *		} field2;
+ *		int field3;
+ *	} __packed;
+ *
+ * This is the expected memory layout of a buffer using iosys_map_rd_field():
+ *
+ * +------------------------------+--------------------------+
+ * | Address                      | Content                  |
+ * +==============================+==========================+
+ * | buffer + 0000                | start of mmapped buffer  |
+ * |                              | pointed by iosys_map     |
+ * +------------------------------+--------------------------+
+ * | ...                          | ...                      |
+ * +------------------------------+--------------------------+
+ * | buffer + ``struct_offset__`` | start of ``struct foo``  |
+ * +------------------------------+--------------------------+
+ * | ...                          | ...                      |
+ * +------------------------------+--------------------------+
+ * | buffer + wwww                | ``foo.field2.inner2``    |
+ * +------------------------------+--------------------------+
+ * | ...                          | ...                      |
+ * +------------------------------+--------------------------+
+ * | buffer + yyyy                | end of ``struct foo``    |
+ * +------------------------------+--------------------------+
+ * | ...                          | ...                      |
+ * +------------------------------+--------------------------+
+ * | buffer + zzzz                | end of mmaped buffer     |
+ * +------------------------------+--------------------------+
+ *
+ * Values automatically calculated by this macro or not needed are denoted by
+ * wwww, yyyy and zzzz. This is the code to read that value:
+ *
+ * .. code-block:: c
+ *
+ *	x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2);
+ *
+ * Returns:
+ * The value read from the mapping.
+ */
+#define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({	\
+	struct_type__ *s;							\
+	iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__),	\
+		     typeof(s->field__));					\
+})
+
+/**
+ * iosys_map_wr_field - Write to a member of a struct in the iosys_map
+ *
+ * @map__:		The iosys_map structure
+ * @struct_offset__:	Offset from the beggining of the map, where the struct
+ *			is located
+ * @struct_type__:	The struct describing the layout of the mapping
+ * @field__:		Member of the struct to read
+ * @val__:		Value to write
+ *
+ * Write a value to the iosys_map considering its layout is described by a C struct
+ * starting at @struct_offset__. The field offset and size is calculated and the
+ * @val__ is written handling possible un-aligned memory accesses. Refer to
+ * iosys_map_rd_field() for expected usage and memory layout.
+ */
+#define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({	\
+	struct_type__ *s;								\
+	iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__),		\
+		     typeof(s->field__), val__);					\
+})
+
 #endif /* __IOSYS_MAP_H__ */