diff mbox series

[1/3] hexdump: Implement macro for converting large buffers

Message ID 20250214162436.241359-2-nnac123@linux.ibm.com (mailing list archive)
State Changes Requested
Headers show
Series Use new for_each macro to create hexdumps | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Guessed tree name to be net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 548 this patch: 548
netdev/build_tools success Errors and warnings before: 26 (+1) this patch: 26 (+1)
netdev/cc_maintainers fail 4 maintainers not CCed: rostedt@goodmis.org senozhatsky@chromium.org pmladek@suse.com john.ogness@linutronix.de
netdev/build_clang success Errors and warnings before: 2287 this patch: 2287
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 15341 this patch: 15341
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 27 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2025-02-15--03-00 (tests: 891)

Commit Message

Nick Child Feb. 14, 2025, 4:24 p.m. UTC
Define for_each_line_in_hex_dump which loops over a buffer and calls
hex_dump_to_buffer for each segment in the buffer. This allows the
caller to decide what to do with the resulting string and is not
limited by a specific printing format like print_hex_dump.

Signed-off-by: Nick Child <nnac123@linux.ibm.com>
---
 include/linux/printk.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

Comments

Nick Child Feb. 14, 2025, 6:33 p.m. UTC | #1
Hi Dave,

Thanks for reviewing,

On 2/14/25 12:00 PM, Dave Marquardt wrote:
> Nick Child <nnac123@linux.ibm.com> writes:
>
>> +	     (i) += (rowsize) == 16 || (rowsize) == 32 ? (rowsize) : 16	\
> Nit: If you left out the (rowsize) == 16 check here you'd still add 16
> to (i).

I was trying to have this translate into "if invalid rowsize was used 
then default to 16" since

hex_dump_to_buffer has a very similar conditional. But I agree, 
logically it looks strange.

If I send a v3 (I also foolishly forgot the v2 tag in this patch), I 
will change this like to

+	     (i) += (rowsize) == 32 ? 32 : 16	\
Simon Horman Feb. 15, 2025, 4:36 p.m. UTC | #2
+ David Laight

On Fri, Feb 14, 2025 at 10:24:34AM -0600, Nick Child wrote:
> Define for_each_line_in_hex_dump which loops over a buffer and calls
> hex_dump_to_buffer for each segment in the buffer. This allows the
> caller to decide what to do with the resulting string and is not
> limited by a specific printing format like print_hex_dump.
> 
> Signed-off-by: Nick Child <nnac123@linux.ibm.com>
> ---
>  include/linux/printk.h | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/include/linux/printk.h b/include/linux/printk.h
> index 4217a9f412b2..559d4bfe0645 100644
> --- a/include/linux/printk.h
> +++ b/include/linux/printk.h
> @@ -755,6 +755,27 @@ enum {
>  extern int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
>  			      int groupsize, char *linebuf, size_t linebuflen,
>  			      bool ascii);
> +/**
> + * for_each_line_in_hex_dump - iterate over buffer, converting into hex ASCII
> + * @i: offset in @buff
> + * @rowsize: number of bytes to print per line; must be 16 or 32
> + * @linebuf: where to put the converted data
> + * @linebuflen: total size of @linebuf, including space for terminating NUL
> + *		IOW >= (@rowsize * 2) + ((@rowsize - 1 / @groupsize)) + 1
> + * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
> + * @buf: data blob to dump
> + * @len: number of bytes in the @buf
> + */
> + #define for_each_line_in_hex_dump(i, rowsize, linebuf, linebuflen, groupsize, \
> +				   buf, len) \
> +	for ((i) = 0;							\
> +	     (i) < (len) &&						\
> +	     hex_dump_to_buffer((unsigned char *)(buf) + (i),		\
> +				min((len) - (i), rowsize),		\
> +				(rowsize), (groupsize), (linebuf),	\
> +				(linebuflen), false);			\
> +	     (i) += (rowsize) == 16 || (rowsize) == 32 ? (rowsize) : 16	\
> +	    )
>  #ifdef CONFIG_PRINTK
>  extern void print_hex_dump(const char *level, const char *prefix_str,
>  			   int prefix_type, int rowsize, int groupsize,

Hi Nick,

When compiling with gcc 7.5.0 (old, but still supported AFAIK) on x86_64
with patch 2/3 (and 1/3) applied I see this:

  CC      lib/hexdump.o
In file included from <command-line>:0:0:
lib/hexdump.c: In function 'print_hex_dump':
././include/linux/compiler_types.h:542:38: error: call to '__compiletime_assert_11' declared with attribute error: min((len) - (i), rowsize) signedness error
  _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
                                      ^
././include/linux/compiler_types.h:523:4: note: in definition of macro '__compiletime_assert'
    prefix ## suffix();    \
    ^~~~~~
././include/linux/compiler_types.h:542:2: note: in expansion of macro '_compiletime_assert'
  _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
  ^~~~~~~~~~~~~~~~~~~
./include/linux/build_bug.h:39:37: note: in expansion of macro 'compiletime_assert'
 #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
                                     ^~~~~~~~~~~~~~~~~~
./include/linux/minmax.h:93:2: note: in expansion of macro 'BUILD_BUG_ON_MSG'
  BUILD_BUG_ON_MSG(!__types_ok(ux, uy),  \
  ^~~~~~~~~~~~~~~~
./include/linux/minmax.h:98:2: note: in expansion of macro '__careful_cmp_once'
  __careful_cmp_once(op, x, y, __UNIQUE_ID(x_), __UNIQUE_ID(y_))
  ^~~~~~~~~~~~~~~~~~
./include/linux/minmax.h:105:19: note: in expansion of macro '__careful_cmp'
 #define min(x, y) __careful_cmp(min, x, y)
                   ^~~~~~~~~~~~~
./include/linux/printk.h:774:5: note: in expansion of macro 'min'
     min((len) - (i), rowsize),  \
     ^~~
lib/hexdump.c:272:2: note: in expansion of macro 'for_each_line_in_hex_dump'
  for_each_line_in_hex_dump(i, rowsize, linebuf, sizeof(linebuf),
  ^~~~~~~~~~~~~~~~~~~~~~~~~

Highlighting the min line in the macro for context, it looks like this:

	min((len) - (i), rowsize)

And in this case the types involved are:

	size_t len
	int i
	int rowsize

This is not a proposal, but I made a quick hack changing they type of rowsize
to size_t and the problem goes away. So I guess it is the type missmatch
between the two arguments to min that needs to be resolved somehow.


FWIIW, you should be able to reproduce this problem fairly easily using
the toolchain here:
https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/7.5.0/
David Laight Feb. 15, 2025, 5:40 p.m. UTC | #3
On Sat, 15 Feb 2025 16:36:12 +0000
Simon Horman <horms@kernel.org> wrote:

> + David Laight
> 
> On Fri, Feb 14, 2025 at 10:24:34AM -0600, Nick Child wrote:
> > Define for_each_line_in_hex_dump which loops over a buffer and calls
> > hex_dump_to_buffer for each segment in the buffer. This allows the
> > caller to decide what to do with the resulting string and is not
> > limited by a specific printing format like print_hex_dump.
> > 
> > Signed-off-by: Nick Child <nnac123@linux.ibm.com>
> > ---
> >  include/linux/printk.h | 21 +++++++++++++++++++++
> >  1 file changed, 21 insertions(+)
> > 
> > diff --git a/include/linux/printk.h b/include/linux/printk.h
> > index 4217a9f412b2..559d4bfe0645 100644
> > --- a/include/linux/printk.h
> > +++ b/include/linux/printk.h
> > @@ -755,6 +755,27 @@ enum {
> >  extern int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
> >  			      int groupsize, char *linebuf, size_t linebuflen,
> >  			      bool ascii);
> > +/**
> > + * for_each_line_in_hex_dump - iterate over buffer, converting into hex ASCII
> > + * @i: offset in @buff
> > + * @rowsize: number of bytes to print per line; must be 16 or 32
> > + * @linebuf: where to put the converted data
> > + * @linebuflen: total size of @linebuf, including space for terminating NUL
> > + *		IOW >= (@rowsize * 2) + ((@rowsize - 1 / @groupsize)) + 1
> > + * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
> > + * @buf: data blob to dump
> > + * @len: number of bytes in the @buf
> > + */
> > + #define for_each_line_in_hex_dump(i, rowsize, linebuf, linebuflen, groupsize, \
> > +				   buf, len) \
> > +	for ((i) = 0;							\
> > +	     (i) < (len) &&						\
> > +	     hex_dump_to_buffer((unsigned char *)(buf) + (i),		\
> > +				min((len) - (i), rowsize),		\
> > +				(rowsize), (groupsize), (linebuf),	\
> > +				(linebuflen), false);			\
> > +	     (i) += (rowsize) == 16 || (rowsize) == 32 ? (rowsize) : 16	\

WTF?
I'm not sure why there is a restriction on 'rowsize' by that increment
makes no sense at all.

> > +	    )
> >  #ifdef CONFIG_PRINTK
> >  extern void print_hex_dump(const char *level, const char *prefix_str,
> >  			   int prefix_type, int rowsize, int groupsize,  
> 
> Hi Nick,
> 
> When compiling with gcc 7.5.0 (old, but still supported AFAIK) on x86_64
> with patch 2/3 (and 1/3) applied I see this:
> 
>   CC      lib/hexdump.o
> In file included from <command-line>:0:0:
> lib/hexdump.c: In function 'print_hex_dump':
> ././include/linux/compiler_types.h:542:38: error: call to '__compiletime_assert_11' declared with attribute error: min((len) - (i), rowsize) signedness error
...
> Highlighting the min line in the macro for context, it looks like this:
> 
> 	min((len) - (i), rowsize)
> 
> And in this case the types involved are:
> 
> 	size_t len
> 	int i
> 	int rowsize

Yep, that should fail for all versions of gcc.
Both 'i' and 'rowsize' should be unsigned types.
In fact all three can be 'unsigned int'.

	David

> 
> This is not a proposal, but I made a quick hack changing they type of rowsize
> to size_t and the problem goes away. So I guess it is the type missmatch
> between the two arguments to min that needs to be resolved somehow.
> 
> 
> FWIIW, you should be able to reproduce this problem fairly easily using
> the toolchain here:
> https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/7.5.0/
David Laight Feb. 15, 2025, 5:46 p.m. UTC | #4
On Sat, 15 Feb 2025 17:40:39 +0000
David Laight <david.laight.linux@gmail.com> wrote:

> On Sat, 15 Feb 2025 16:36:12 +0000
> Simon Horman <horms@kernel.org> wrote:
> 
> > + David Laight
> > 
> > On Fri, Feb 14, 2025 at 10:24:34AM -0600, Nick Child wrote:  
> > > Define for_each_line_in_hex_dump which loops over a buffer and calls
> > > hex_dump_to_buffer for each segment in the buffer. This allows the
> > > caller to decide what to do with the resulting string and is not
> > > limited by a specific printing format like print_hex_dump.
> > > 
> > > Signed-off-by: Nick Child <nnac123@linux.ibm.com>
> > > ---
> > >  include/linux/printk.h | 21 +++++++++++++++++++++
> > >  1 file changed, 21 insertions(+)
> > > 
> > > diff --git a/include/linux/printk.h b/include/linux/printk.h
> > > index 4217a9f412b2..559d4bfe0645 100644
> > > --- a/include/linux/printk.h
> > > +++ b/include/linux/printk.h
> > > @@ -755,6 +755,27 @@ enum {
> > >  extern int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
> > >  			      int groupsize, char *linebuf, size_t linebuflen,
> > >  			      bool ascii);
> > > +/**
> > > + * for_each_line_in_hex_dump - iterate over buffer, converting into hex ASCII
> > > + * @i: offset in @buff
> > > + * @rowsize: number of bytes to print per line; must be 16 or 32
> > > + * @linebuf: where to put the converted data
> > > + * @linebuflen: total size of @linebuf, including space for terminating NUL
> > > + *		IOW >= (@rowsize * 2) + ((@rowsize - 1 / @groupsize)) + 1
> > > + * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
> > > + * @buf: data blob to dump
> > > + * @len: number of bytes in the @buf
> > > + */
> > > + #define for_each_line_in_hex_dump(i, rowsize, linebuf, linebuflen, groupsize, \
> > > +				   buf, len) \
> > > +	for ((i) = 0;							\
> > > +	     (i) < (len) &&						\
> > > +	     hex_dump_to_buffer((unsigned char *)(buf) + (i),		\
> > > +				min((len) - (i), rowsize),		\
> > > +				(rowsize), (groupsize), (linebuf),	\
> > > +				(linebuflen), false);			\
> > > +	     (i) += (rowsize) == 16 || (rowsize) == 32 ? (rowsize) : 16	\  
> > > +	    )
> > >  #ifdef CONFIG_PRINTK
> > >  extern void print_hex_dump(const char *level, const char *prefix_str,
> > >  			   int prefix_type, int rowsize, int groupsize,    
> > 
> > Hi Nick,
> > 
> > When compiling with gcc 7.5.0 (old, but still supported AFAIK) on x86_64
> > with patch 2/3 (and 1/3) applied I see this:
> > 
> >   CC      lib/hexdump.o
> > In file included from <command-line>:0:0:
> > lib/hexdump.c: In function 'print_hex_dump':
> > ././include/linux/compiler_types.h:542:38: error: call to '__compiletime_assert_11' declared with attribute error: min((len) - (i), rowsize) signedness error  
> ...
> > Highlighting the min line in the macro for context, it looks like this:
> > 
> > 	min((len) - (i), rowsize)
> > 
> > And in this case the types involved are:
> > 
> > 	size_t len
> > 	int i
> > 	int rowsize  
> 
> Yep, that should fail for all versions of gcc.
> Both 'i' and 'rowsize' should be unsigned types.
> In fact all three can be 'unsigned int'.

Thinking a bit more.
If the compiler can determine that 'rowsize >= 0' then the test will pass.
More modern compilers do better value tracking so that might be enough
to stop the compiler 'bleating'.

	David

> 
> 	David
> 
> > 
> > This is not a proposal, but I made a quick hack changing they type of rowsize
> > to size_t and the problem goes away. So I guess it is the type missmatch
> > between the two arguments to min that needs to be resolved somehow.
> > 
> > 
> > FWIIW, you should be able to reproduce this problem fairly easily using
> > the toolchain here:
> > https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/7.5.0/  
>
Simon Horman Feb. 16, 2025, 9:32 a.m. UTC | #5
On Sat, Feb 15, 2025 at 05:46:35PM +0000, David Laight wrote:
> On Sat, 15 Feb 2025 17:40:39 +0000
> David Laight <david.laight.linux@gmail.com> wrote:
> 
> > On Sat, 15 Feb 2025 16:36:12 +0000
> > Simon Horman <horms@kernel.org> wrote:
> > 
> > > + David Laight
> > > 
> > > On Fri, Feb 14, 2025 at 10:24:34AM -0600, Nick Child wrote:  
> > > > Define for_each_line_in_hex_dump which loops over a buffer and calls
> > > > hex_dump_to_buffer for each segment in the buffer. This allows the
> > > > caller to decide what to do with the resulting string and is not
> > > > limited by a specific printing format like print_hex_dump.
> > > > 
> > > > Signed-off-by: Nick Child <nnac123@linux.ibm.com>
> > > > ---
> > > >  include/linux/printk.h | 21 +++++++++++++++++++++
> > > >  1 file changed, 21 insertions(+)
> > > > 
> > > > diff --git a/include/linux/printk.h b/include/linux/printk.h
> > > > index 4217a9f412b2..559d4bfe0645 100644
> > > > --- a/include/linux/printk.h
> > > > +++ b/include/linux/printk.h
> > > > @@ -755,6 +755,27 @@ enum {
> > > >  extern int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
> > > >  			      int groupsize, char *linebuf, size_t linebuflen,
> > > >  			      bool ascii);
> > > > +/**
> > > > + * for_each_line_in_hex_dump - iterate over buffer, converting into hex ASCII
> > > > + * @i: offset in @buff
> > > > + * @rowsize: number of bytes to print per line; must be 16 or 32
> > > > + * @linebuf: where to put the converted data
> > > > + * @linebuflen: total size of @linebuf, including space for terminating NUL
> > > > + *		IOW >= (@rowsize * 2) + ((@rowsize - 1 / @groupsize)) + 1
> > > > + * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
> > > > + * @buf: data blob to dump
> > > > + * @len: number of bytes in the @buf
> > > > + */
> > > > + #define for_each_line_in_hex_dump(i, rowsize, linebuf, linebuflen, groupsize, \
> > > > +				   buf, len) \
> > > > +	for ((i) = 0;							\
> > > > +	     (i) < (len) &&						\
> > > > +	     hex_dump_to_buffer((unsigned char *)(buf) + (i),		\
> > > > +				min((len) - (i), rowsize),		\
> > > > +				(rowsize), (groupsize), (linebuf),	\
> > > > +				(linebuflen), false);			\
> > > > +	     (i) += (rowsize) == 16 || (rowsize) == 32 ? (rowsize) : 16	\  
> > > > +	    )
> > > >  #ifdef CONFIG_PRINTK
> > > >  extern void print_hex_dump(const char *level, const char *prefix_str,
> > > >  			   int prefix_type, int rowsize, int groupsize,    
> > > 
> > > Hi Nick,
> > > 
> > > When compiling with gcc 7.5.0 (old, but still supported AFAIK) on x86_64
> > > with patch 2/3 (and 1/3) applied I see this:
> > > 
> > >   CC      lib/hexdump.o
> > > In file included from <command-line>:0:0:
> > > lib/hexdump.c: In function 'print_hex_dump':
> > > ././include/linux/compiler_types.h:542:38: error: call to '__compiletime_assert_11' declared with attribute error: min((len) - (i), rowsize) signedness error  
> > ...
> > > Highlighting the min line in the macro for context, it looks like this:
> > > 
> > > 	min((len) - (i), rowsize)
> > > 
> > > And in this case the types involved are:
> > > 
> > > 	size_t len
> > > 	int i
> > > 	int rowsize  
> > 
> > Yep, that should fail for all versions of gcc.
> > Both 'i' and 'rowsize' should be unsigned types.
> > In fact all three can be 'unsigned int'.

To give a bit more context, a complication changing the types is that the
type of len and rowsise (but not i) is in the signature of the calling
function, print_hex_dump(). And I believe that function is widely used
throughout the tree.

> 
> Thinking a bit more.
> If the compiler can determine that 'rowsize >= 0' then the test will pass.
> More modern compilers do better value tracking so that might be enough
> to stop the compiler 'bleating'.

FTR, I did not see this with GCC 14.2.0 (or clang 19.1.7).
David Laight Feb. 16, 2025, 11:24 a.m. UTC | #6
On Sun, 16 Feb 2025 09:32:04 +0000
Simon Horman <horms@kernel.org> wrote:

>...
> > > Yep, that should fail for all versions of gcc.
> > > Both 'i' and 'rowsize' should be unsigned types.
> > > In fact all three can be 'unsigned int'.  
> 
> To give a bit more context, a complication changing the types is that the
> type of len and rowsise (but not i) is in the signature of the calling
> function, print_hex_dump(). And I believe that function is widely used
> throughout the tree.

Doesn't matter, nothing with assign the address of the function to a
variable so changing the types (to unsigned) doesn't affect any callers.
The values better be positive!

I just changed the prototypes (include/linux/printk.h) to make both
rowsize and groupsize 'unsigned int'.
The same change in lib/hexdump.c + changing the local 'i, linelen, remaining'
to unsigned int and it all compiled.

FWIW that hexdump code is pretty horrid (especially if groupsize != 1).

	David
Nick Child Feb. 17, 2025, 3:09 p.m. UTC | #7
Thank you David and Simon for testing and review!

On Sun, Feb 16, 2025 at 11:24:30AM +0000, David Laight wrote:
> 
> I just changed the prototypes (include/linux/printk.h) to make both
> rowsize and groupsize 'unsigned int'.
> The same change in lib/hexdump.c + changing the local 'i, linelen, remaining'
> to unsigned int and it all compiled.
> 
> FWIW that hexdump code is pretty horrid (especially if groupsize != 1).
> 

Given this discussion and my own head-scratching, I think I will take a
closer look at hex_dump_to_buffer.

I was trying to avoid editing this function due the number of callers it
has across the kernel. But I think we can get away with keeping the
API (but change args to uint's) and editing the body of the function
to always iterate byte-by-byte, addding space chars where necessary. At the
cost of a few more cycles, this will allow for more dynamic values
for rowsize and groupsize and shorten the code up a bit. This would also
address the "Side question" in my cover letter. Will send a v3
regardless if I can figure that out or not.

The return value of hex_dump_to_buffer on error still irks me a bit but
I don't think that can easily be changed.

Thanks again!
Paolo Abeni Feb. 18, 2025, 12:31 p.m. UTC | #8
On 2/17/25 4:09 PM, Nick Child wrote:
> Thank you David and Simon for testing and review!
> 
> On Sun, Feb 16, 2025 at 11:24:30AM +0000, David Laight wrote:
>>
>> I just changed the prototypes (include/linux/printk.h) to make both
>> rowsize and groupsize 'unsigned int'.
>> The same change in lib/hexdump.c + changing the local 'i, linelen, remaining'
>> to unsigned int and it all compiled.
>>
>> FWIW that hexdump code is pretty horrid (especially if groupsize != 1).
>>
> 
> Given this discussion and my own head-scratching, I think I will take a
> closer look at hex_dump_to_buffer.
> 
> I was trying to avoid editing this function due the number of callers it
> has across the kernel. But I think we can get away with keeping the
> API (but change args to uint's) and editing the body of the function
> to always iterate byte-by-byte, addding space chars where necessary. At the
> cost of a few more cycles, this will allow for more dynamic values
> for rowsize and groupsize and shorten the code up a bit. This would also
> address the "Side question" in my cover letter. Will send a v3
> regardless if I can figure that out or not.
> 
> The return value of hex_dump_to_buffer on error still irks me a bit but
> I don't think that can easily be changed.

For the new versions, please:

- respect the 24h grace period:

https://elixir.bootlin.com/linux/v6.11.8/source/Documentation/process/maintainer-netdev.rst#L15

- add the target tree in the subj prefix (in this case 'net-next')

- ensure all the patches in the series have consistent subj prefix,
otherwise patchwork will be fooled.

I think it would be better to avoid modifying hex_dump_to_buffer(), if
not necessary, and I think you could do so either changing the type used
in patch 2 or even dropping such patch.

/P
diff mbox series

Patch

diff --git a/include/linux/printk.h b/include/linux/printk.h
index 4217a9f412b2..559d4bfe0645 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -755,6 +755,27 @@  enum {
 extern int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
 			      int groupsize, char *linebuf, size_t linebuflen,
 			      bool ascii);
+/**
+ * for_each_line_in_hex_dump - iterate over buffer, converting into hex ASCII
+ * @i: offset in @buff
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @linebuf: where to put the converted data
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
+ *		IOW >= (@rowsize * 2) + ((@rowsize - 1 / @groupsize)) + 1
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ */
+ #define for_each_line_in_hex_dump(i, rowsize, linebuf, linebuflen, groupsize, \
+				   buf, len) \
+	for ((i) = 0;							\
+	     (i) < (len) &&						\
+	     hex_dump_to_buffer((unsigned char *)(buf) + (i),		\
+				min((len) - (i), rowsize),		\
+				(rowsize), (groupsize), (linebuf),	\
+				(linebuflen), false);			\
+	     (i) += (rowsize) == 16 || (rowsize) == 32 ? (rowsize) : 16	\
+	    )
 #ifdef CONFIG_PRINTK
 extern void print_hex_dump(const char *level, const char *prefix_str,
 			   int prefix_type, int rowsize, int groupsize,