[06/25] Add struct scratch_buffer and its internal helper functions

Message ID 7a6fe503fb764beee3d5b89662d3bbf65242161c.1425285061.git.fweimer@redhat.com
State Superseded
Headers

Commit Message

Florian Weimer March 1, 2015, 2:28 p.m. UTC
  These will be used from NSS modules, so they have to be exported.
---
 include/scratch_buffer.h                           | 133 ++++++++++++++++++
 malloc/Makefile                                    |   6 +-
 malloc/Versions                                    |   5 +
 malloc/scratch_buffer_grow.c                       |  52 +++++++
 malloc/scratch_buffer_grow_preserve.c              |  65 +++++++++
 malloc/scratch_buffer_set_array_size.c             |  61 ++++++++
 malloc/tst-scratch_buffer.c                        | 155 +++++++++++++++++++++
 sysdeps/unix/sysv/linux/aarch64/libc.abilist       |   5 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist         |   5 +
 sysdeps/unix/sysv/linux/arm/libc.abilist           |   5 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist          |   5 +
 sysdeps/unix/sysv/linux/i386/libc.abilist          |   5 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist          |   5 +
 sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist |   5 +
 sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist   |   5 +
 sysdeps/unix/sysv/linux/microblaze/libc.abilist    |   5 +
 .../unix/sysv/linux/mips/mips32/fpu/libc.abilist   |   5 +
 .../unix/sysv/linux/mips/mips32/nofpu/libc.abilist |   5 +
 .../unix/sysv/linux/mips/mips64/n32/libc.abilist   |   5 +
 .../unix/sysv/linux/mips/mips64/n64/libc.abilist   |   5 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist         |   5 +
 .../sysv/linux/powerpc/powerpc32/fpu/libc.abilist  |   5 +
 .../linux/powerpc/powerpc32/nofpu/libc.abilist     |   5 +
 .../unix/sysv/linux/powerpc/powerpc64/libc.abilist |   5 +
 sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist  |   5 +
 sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist  |   5 +
 sysdeps/unix/sysv/linux/sh/libc.abilist            |   5 +
 sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist |   5 +
 sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist |   5 +
 .../sysv/linux/tile/tilegx/tilegx32/libc.abilist   |   5 +
 .../sysv/linux/tile/tilegx/tilegx64/libc.abilist   |   5 +
 sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist  |   5 +
 sysdeps/unix/sysv/linux/x86_64/64/libc.abilist     |   5 +
 sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist    |   5 +
 34 files changed, 610 insertions(+), 2 deletions(-)
 create mode 100644 include/scratch_buffer.h
 create mode 100644 malloc/scratch_buffer_grow.c
 create mode 100644 malloc/scratch_buffer_grow_preserve.c
 create mode 100644 malloc/scratch_buffer_set_array_size.c
 create mode 100644 malloc/tst-scratch_buffer.c
  

Comments

Andreas Schwab March 2, 2015, 9:35 a.m. UTC | #1
Florian Weimer <fweimer@redhat.com> writes:

> +#pragma once

Don't use pragma once.

> +  GLIBC_2.22 {
> +    __libc_scratch_buffer_grow;
> +    __libc_scratch_buffer_grow_preserve;
> +    __libc_scratch_buffer_set_array_size;

Are they supposed to be used outside of glibc?  Since scratch_buffer.h
isn't installed, it doesn't look like.

Andreas.
  
Florian Weimer March 2, 2015, 9:40 a.m. UTC | #2
On 03/02/2015 10:35 AM, Andreas Schwab wrote:
> Florian Weimer <fweimer@redhat.com> writes:
> 
>> +#pragma once
> 
> Don't use pragma once.

Why?  The GCC implementation was fixed ages ago, and it's not an
installed header file.

>> +  GLIBC_2.22 {
>> +    __libc_scratch_buffer_grow;
>> +    __libc_scratch_buffer_grow_preserve;
>> +    __libc_scratch_buffer_set_array_size;
> 
> Are they supposed to be used outside of glibc?  Since scratch_buffer.h
> isn't installed, it doesn't look like.

See the commit message.  nscd and some of the NSS modules need these
symbols.
  
Andreas Schwab March 2, 2015, 9:45 a.m. UTC | #3
Florian Weimer <fweimer@redhat.com> writes:

> See the commit message.  nscd and some of the NSS modules need these
> symbols.

Then use GLIBC_PRIVATE.

Andreas.
  
Rich Felker March 2, 2015, 5:42 p.m. UTC | #4
On Mon, Mar 02, 2015 at 10:40:46AM +0100, Florian Weimer wrote:
> On 03/02/2015 10:35 AM, Andreas Schwab wrote:
> > Florian Weimer <fweimer@redhat.com> writes:
> > 
> >> +#pragma once
> > 
> > Don't use pragma once.
> 
> Why?  The GCC implementation was fixed ages ago, and it's not an
> installed header file.

If for no other reason, cargo-culting. This is the wrong way to
achieve multiple-include protection (it's non-portable and has a
perfectly portable way to do the same thing) and programmers will see
and copy it into non-glibc code.

Rich
  
Florian Weimer March 2, 2015, 5:54 p.m. UTC | #5
On 03/02/2015 06:42 PM, Rich Felker wrote:
> On Mon, Mar 02, 2015 at 10:40:46AM +0100, Florian Weimer wrote:
>> On 03/02/2015 10:35 AM, Andreas Schwab wrote:
>>> Florian Weimer <fweimer@redhat.com> writes:
>>>
>>>> +#pragma once
>>>
>>> Don't use pragma once.
>>
>> Why?  The GCC implementation was fixed ages ago, and it's not an
>> installed header file.
> 
> If for no other reason, cargo-culting. This is the wrong way to
> achieve multiple-include protection (it's non-portable and has a
> perfectly portable way to do the same thing)

It's portable to anything except Solaris Studio.  It's supported by the
EDG front end, so you have to do actual work to build a compiler that
doesn't support it.  (Not that it matters in this case because it is an
internal header anyway.)

Compare this to most C99 or even C11 features, which keep most of our
code in a world of its own (which is fine from my point of view,
pretend-portability without actual users doesn't work so well in practice).

And no, there is nothing else which provides a similar capability to
#pragma once, in a way that is equally portable in practice.

> and programmers will see and copy it into non-glibc code.

And that's a good thing because it avoids real bugs.

But of course I'll add a traditional #include guard.  Okay to commit
with such a change?
  
Paul Eggert March 2, 2015, 7:33 p.m. UTC | #6
On 03/01/2015 06:28 AM, Florian Weimer wrote:
> +   The non-inlined functions are implemented in such a way that it is
> +   possible to change the size of the pre-allocated buffer without
> +   impacting ABI.

Why does this matter?  The ABI is not exported to users, right?  The 
comment should explain why the ABI business matters.

>
> +#define SCRATCH_BUFFER_ALIGNMENT \
> +  __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))

This should use __attribute__ ((aligned (alignof (max_align_t)))), at 
least on C11 platforms.

> +struct scratch_buffer {
> +  void *data;    /* Pointer to the beginning of the scratch area.  */
> +  size_t length; /* Allocated space at the data pointer, in bytes.  */
> +  char __space[1024 - sizeof (size_t)] SCRATCH_BUFFER_ALIGNMENT;
> +} SCRATCH_BUFFER_ALIGNMENT;

Why are there two SCRATCH_BUFFER_ALIGNMENTs there?  One suffices, no?

Why 1024?  And why "- sizeof (size_t)"?  A comment should say.

> +/* Grows *BUFFER by some arbitrary amount.  The buffer contents is NOT
> +   preserved.  Returns true on success, fails on allocation failure
> +   (in which case the old buffer is freed).  On success, the new
> +   buffer is slightly larger (by at least 16 bytes) than the previous
> +   size.  On failure, *BUFFER is deallocated, but remains in a
> +   free-able state.  */

Why 16?  The comment should say.

Shouldn't the caller have some say on how big to grow the buffer?  I can 
see a caller knowing how much space it'll need.  As things stand, such a 
caller needs to repeatedly grow the buffer until it's big enough, which 
is awkward.  Hmm, I see that scratch_buffer_set_array_size lets one set 
the array size to any value, but there's no variant of 
scratch_buffer_set_array_size that preserves the buffer.

On failure this function sets errno, right?  The comment should say this.

> +/* Grows *BUFFER so that it can store at least NELEM elemnts of SIZE
> +   bytes.  The buffer contents is NOT preserved.  Returns true on
> +   success, fails on allocation failure (in which case the old buffer
> +   is freed, but *BUFFER remains a free-able state).  */

Similar remark about errno.  Also, is this function allowed to shrink 
*BUFFER?  Also, is SIZE allowed to be zero?  The comment should say.

> +  size_t new_length = buffer->length * 2;

In Gnulib we originally did it this way, but nowadays we grow by a 
factor of 1.5 (new_length = old_length + old_length / 2 + 1) rather than 
by a factor of 2, as this was less jerky for large buffers. Perhaps do 
something similar here?

> +  size_t size_max_square_root = ((size_t)1) << (sizeof (size_t) * 4);
> +  /* Avoid overflow check if both values are small. */
> +  if (nelem >= size_max_square_root || size >= size_max_square_root)

"4" is too much a mystery here and should be spelled out as CHAR_BIT / 
2.  Also, this is smaller and faster when done this way:

    if ((nelem | size) >> (sizeof (size_t) * CHAR_BIT / 2) != 0)



> +      if (nelem != 0 && size > SIZE_MAX / nelem)

This is a run-time integer division.  It can be a bit faster to do the 
division at compile-time.  Gnulib does this by computing 'SIZE_MAX / 
size' in an inline function; 'size' is normally a constant (and must be 
nonzero in Gnulib, which is a reasonable restriction here too).  With 
this optimization, Gnulib doesn't need to mess with size square roots 
either, another minor performance win.
  
Florian Weimer March 20, 2015, 3:08 p.m. UTC | #7
Paul,

thank you for your extensive comments.

On 03/02/2015 08:33 PM, Paul Eggert wrote:
> On 03/01/2015 06:28 AM, Florian Weimer wrote:
>> +   The non-inlined functions are implemented in such a way that it is
>> +   possible to change the size of the pre-allocated buffer without
>> +   impacting ABI.
> 
> Why does this matter?  The ABI is not exported to users, right?  The
> comment should explain why the ABI business matters.

It helps slightly with online updates.  I don't want to introduce any
additional problems for them if we fine-tune the buffer size.

>> +#define SCRATCH_BUFFER_ALIGNMENT \
>> +  __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))
> 
> This should use __attribute__ ((aligned (alignof (max_align_t)))), at
> least on C11 platforms.

Hmm.  Isn't intmax_t enough?

>> +struct scratch_buffer {
>> +  void *data;    /* Pointer to the beginning of the scratch area.  */
>> +  size_t length; /* Allocated space at the data pointer, in bytes.  */
>> +  char __space[1024 - sizeof (size_t)] SCRATCH_BUFFER_ALIGNMENT;
>> +} SCRATCH_BUFFER_ALIGNMENT;
> 
> Why are there two SCRATCH_BUFFER_ALIGNMENTs there?  One suffices, no?

Right.

> Why 1024?

That's the magic constant most of the existing code (in NSS) used.

>  And why "- sizeof (size_t)"?  A comment should say.

It turns into - 2 * sizeof (size_t) after doubling for the first heap
allocation, which is the current size of the malloc metadata.  I'll drop
it, it's probably too cute.  If we want to optimize the malloc case,
checking for lack of malloc overloading and then extending transparently
to malloc_usable_size bytes (if no malloc override is in place) seems
more reasonable.

>> +/* Grows *BUFFER by some arbitrary amount.  The buffer contents is NOT
>> +   preserved.  Returns true on success, fails on allocation failure
>> +   (in which case the old buffer is freed).  On success, the new
>> +   buffer is slightly larger (by at least 16 bytes) than the previous
>> +   size.  On failure, *BUFFER is deallocated, but remains in a
>> +   free-able state.  */
> 
> Why 16?  The comment should say.

I bumped it to 512 and added an explanation:

   scratch_buffer_grow and scratch_buffer_grow_preserve are guaranteed
   to grow the buffer by at least 512 bytes.  This means that when
   using the scratch buffer as a backing store for a non-character
   array whose element size, in bytes, is 512 or smaller, the scratch
   buffer only has to grown once to make room for at least one more
   element.

> Shouldn't the caller have some say on how big to grow the buffer?  I can
> see a caller knowing how much space it'll need.

Most callers don't.  And for the very few remaining ones, there is
scratch_buffer_set_array_size.

>  As things stand, such a
> caller needs to repeatedly grow the buffer until it's big enough, which
> is awkward.

It's the main use case in NSS because that's the way gethostbyname_r and
friends were specified.  You don't get to know the required buffer size
on failure.

> Hmm, I see that scratch_buffer_set_array_size lets one set
> the array size to any value, but there's no variant of
> scratch_buffer_set_array_size that preserves the buffer.

It has not come up yet.

> On failure this function sets errno, right?  The comment should say this.

Right.

>> +/* Grows *BUFFER so that it can store at least NELEM elemnts of SIZE
>> +   bytes.  The buffer contents is NOT preserved.  Returns true on
>> +   success, fails on allocation failure (in which case the old buffer
>> +   is freed, but *BUFFER remains a free-able state).  */
> 
> Similar remark about errno.  Also, is this function allowed to shrink
> *BUFFER?  Also, is SIZE allowed to be zero?  The comment should say.

What about this?

/* Grows *BUFFER so that it can store at least NELEM elements of SIZE
   bytes.  The buffer contents is NOT preserved.  Both NELEM and SIZE
   can be zero.  It is unspecified wheter this function can reduce the
   array size.  It returns true on success, false on allocation
   failure (in which case the old buffer is freed, but *BUFFER remains
   a free-able state, and errno is set).  */

>> +  size_t new_length = buffer->length * 2;
> 
> In Gnulib we originally did it this way, but nowadays we grow by a
> factor of 1.5 (new_length = old_length + old_length / 2 + 1) rather than
> by a factor of 2, as this was less jerky for large buffers. Perhaps do
> something similar here?

I think we should leave the fine-tuning to a later stage.  See also the
comment above about malloc_usable_size.

The original code used a growth factor of 3 during the alloca phase, and
2 during the malloc phase.  (After the i386 ABI change for stack
alignment, it's effectively 2 and 2.)

>> +  size_t size_max_square_root = ((size_t)1) << (sizeof (size_t) * 4);
>> +  /* Avoid overflow check if both values are small. */
>> +  if (nelem >= size_max_square_root || size >= size_max_square_root)
> 
> "4" is too much a mystery here and should be spelled out as CHAR_BIT /
> 2.  Also, this is smaller and faster when done this way:
> 
>    if ((nelem | size) >> (sizeof (size_t) * CHAR_BIT / 2) != 0)

Thanks, that's indeed nicer.

In the end, it will best what the compiler will be able to turn in a
multiply-with-overflow-check (which most CPUs provide, but not MIPS, so
it was never standardized as part of C).  Very recent GCC has
__builtin_umull_overflow, so maybe we can use that one day.

>> +      if (nelem != 0 && size > SIZE_MAX / nelem)
> 
> This is a run-time integer division.  It can be a bit faster to do the
> division at compile-time.

I know.  G++ uses a constant which is rounded down to make sure the
constant can be stored directly in the instruction stream on RISC.  The
exact value is often very difficult to encode.

> Gnulib does this by computing 'SIZE_MAX /
> size' in an inline function; 'size' is normally a constant (and must be
> nonzero in Gnulib, which is a reasonable restriction here too).  With
> this optimization, Gnulib doesn't need to mess with size square roots
> either, another minor performance win.

It's hard to tell what's better here.  Even on 32 bit, you need at least
65,536 array elements before the division kicks in.  The current callers
do not call this function repeatedly in a loop.  That's why I think it's
reasonable to simplify the call site instead.  And with
__builtin_umull_overflow, the division will go away eventually anyway.

Empirically, I'm pretty certain the division does not matter
performance-wise.  On x86_64 and i386, the old alloca code had an
integer division, too, and no one complained about its performance.
  
Paul Eggert March 20, 2015, 4:29 p.m. UTC | #8
Florian Weimer wrote:
>>> +#define SCRATCH_BUFFER_ALIGNMENT \
>>> +  __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))
>>
>> This should use __attribute__ ((aligned (alignof (max_align_t)))), at
>> least on C11 platforms.
>
> Hmm.  Isn't intmax_t enough?

I don't know about all the platforms, but in theory C11 says one should use 
max_align_t and that types other than intmax_t might have alignments stricter 
than that needed by intmax_t.

>     array whose element size, in bytes, is 512 or smaller, the scratch
>     buffer only has to grown once to make room for at least one more

grown -> grow

> /* Grows *BUFFER so that it can store at least NELEM elements of SIZE

Grows -> grow

>     bytes.  The buffer contents is NOT preserved.  Both NELEM and SIZE

is NOT -> are NOT

>     can be zero.  It is unspecified wheter this function can reduce the

wheter -> whether

>     array size.  It returns true on success, false on allocation

It returns -> Return

>     failure (in which case the old buffer is freed, but *BUFFER remains
>     a free-able state, and errno is set).  */

a free-able -> in a free-able

> Very recent GCC has
> __builtin_umull_overflow, so maybe we can use that one day.

Yes, that's the plan in Gnulib too.

> Empirically, I'm pretty certain the division does not matter
> performance-wise.

Even on platforms where integer division is a subroutine?  Perhaps glibc isn't 
meant for such platforms, but still....
  
Ondrej Bilka April 3, 2015, 4:25 p.m. UTC | #9
On Fri, Mar 20, 2015 at 04:08:39PM +0100, Florian Weimer wrote:
> 
> >> +  size_t new_length = buffer->length * 2;
> > 
> > In Gnulib we originally did it this way, but nowadays we grow by a
> > factor of 1.5 (new_length = old_length + old_length / 2 + 1) rather than
> > by a factor of 2, as this was less jerky for large buffers. Perhaps do
> > something similar here?
> 
> I think we should leave the fine-tuning to a later stage.  See also the
> comment above about malloc_usable_size.
> 
> The original code used a growth factor of 3 during the alloca phase, and
> 2 during the malloc phase.  (After the i386 ABI change for stack
> alignment, it's effectively 2 and 2.)
> 
That does not make much sense. Its mostly used in funcions that cannot
realloc array themself so we double space until it fits. As its
temporary its better to overallocate than try save space.

You could use factor 16 to improve performance. If large buffers are
concern then add treshold after which you use 1.5 but I doubt that you
reach 1MB with functions used.
  
Florian Weimer April 8, 2015, 8:03 p.m. UTC | #10
On 04/03/2015 06:25 PM, Ondřej Bílka wrote:

>> The original code used a growth factor of 3 during the alloca phase, and
>> 2 during the malloc phase.  (After the i386 ABI change for stack
>> alignment, it's effectively 2 and 2.)

> That does not make much sense. Its mostly used in funcions that cannot
> realloc array themself so we double space until it fits. As its
> temporary its better to overallocate than try save space.

I suppose that could be true.  There are two exceptions: When we cross
the trim threshold, and when we cross the mmap threshold.  Both could be
quite bad performance-wise.  I think we could come up with a sizing
strategy that avoids that—or change NSS to allocate the buffer at the
point where the size is known (my preference).

However, it's true that it was more important to conserve space when the
code used alloca.

Thanks for your review of five of the other patches; I committed them
with suitable ChangeLog entries.
  
David Miller April 9, 2015, 2:56 a.m. UTC | #11
From: Florian Weimer <fweimer@redhat.com>
Date: Wed, 08 Apr 2015 22:03:05 +0200

> Thanks for your review of five of the other patches; I committed them
> with suitable ChangeLog entries.

I'm getting a lot of warnings from the scratch_buffer test case on
32-bit sparc, which fails the compile during make check.

The first problem is that 1ULL << 32 doesn't fit in a size_t,
therefore gcc complains about the value being implicitly
truncated to an unsigned type.

For that, I think these >=1<<32 value tests should simply be elided on
32-bit systems.

Secondly I get inlining failure warnings for the scratch_buffer_free
calls in do_test().  I do not have a recommendation for how to solve
that.  Frankly I think if something can't be inlined, in many cases
that's fine and opaque details of gcc's inlining heuristics shouldn't
fail the build ever.

These warnings failing the build are generally very frustrating, and
they have been so for those of us who have limited time to work on
glibc arch maintainence.  We come in to attack a specific problem and
find that we can't even begin to do so because the tree doesn't even
build due to some obscure warning that the developer who installed the
warning causing change doesn't even see.

It's not like we had a one big project to clean up the existing
warnings and then things got better.  This is a continual and ongoing
problem that is pervasive and doesn't improve, as new changes getting
installed are causing new warnings for someone all the time.
  
Florian Weimer April 9, 2015, 6:05 a.m. UTC | #12
On 04/09/2015 04:56 AM, David Miller wrote:

> The first problem is that 1ULL << 32 doesn't fit in a size_t,
> therefore gcc complains about the value being implicitly
> truncated to an unsigned type.
> 
> For that, I think these >=1<<32 value tests should simply be elided on
> 32-bit systems.

It's just a matter of adding a cast to size_t, at least on i386 with GCC
4.9.2.

> Secondly I get inlining failure warnings for the scratch_buffer_free
> calls in do_test().  I do not have a recommendation for how to solve
> that.  Frankly I think if something can't be inlined, in many cases
> that's fine and opaque details of gcc's inlining heuristics shouldn't
> fail the build ever.

What's the exact failure?  Does it refuse to inline because it's not a
hot path?

I could add __always_inline, which should address, at the cost of worse
code.  Or I could move scratch_buffer_free out of line.

> These warnings failing the build are generally very frustrating, and
> they have been so for those of us who have limited time to work on
> glibc arch maintainence.  We come in to attack a specific problem and
> find that we can't even begin to do so because the tree doesn't even
> build due to some obscure warning that the developer who installed the
> warning causing change doesn't even see.

Yes, I cautioned against enabling optimizer-dependent warnings (which
includes certain inline failure warnings) for -Werror because they are a
pain to deal with.
  

Patch

diff --git a/include/scratch_buffer.h b/include/scratch_buffer.h
new file mode 100644
index 0000000..57117b6
--- /dev/null
+++ b/include/scratch_buffer.h
@@ -0,0 +1,133 @@ 
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+/* Scratch buffers with a default stack allocation and fallback to
+   heap allocation.  It is expected that this function is used in this
+   way:
+
+     struct scratch_buffer tmpbuf;
+     scratch_buffer_init (&tmpbuf);
+
+     while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
+       if (!scratch_buffer_grow (&tmpbuf))
+	 return -1;
+
+     scratch_buffer_free (&tmpbuf);
+     return 0;
+
+   The allocation functions (scratch_buffer_grow,
+   scratch_buffer_grow_preserve, scratch_buffer_set_array_size) make
+   sure that the heap allocation, if any, is freed, so that the code
+   above does not have a memory leak.  The buffer still remains in a
+   state that can be deallocated using scratch_buffer_free, so a loop
+   like this is valid as well:
+
+     struct scratch_buffer tmpbuf;
+     scratch_buffer_init (&tmpbuf);
+
+     while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
+       if (!scratch_buffer_grow (&tmpbuf))
+	 break;
+
+     scratch_buffer_free (&tmpbuf);
+
+   The non-inlined functions are implemented in such a way that it is
+   possible to change the size of the pre-allocated buffer without
+   impacting ABI.
+*/
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define SCRATCH_BUFFER_ALIGNMENT \
+  __attribute__ ((aligned (__alignof__ (union {void *p; double d;}))))
+
+/* Scratch buffer.  Must be initialized with scratch_buffer_init
+   before its use.  */
+struct scratch_buffer {
+  void *data;    /* Pointer to the beginning of the scratch area.  */
+  size_t length; /* Allocated space at the data pointer, in bytes.  */
+  char __space[1024 - sizeof (size_t)] SCRATCH_BUFFER_ALIGNMENT;
+} SCRATCH_BUFFER_ALIGNMENT;
+
+/* Initializes *BUFFER so that BUFFER->data points to BUFFER->__space
+   and BUFFER->length reflects the available space.  */
+static inline void
+scratch_buffer_init (struct scratch_buffer *buffer)
+  __attribute__ ((always_inline));
+static inline void
+scratch_buffer_init (struct scratch_buffer *buffer)
+{
+  buffer->data = buffer->__space;
+  buffer->length = sizeof (buffer->__space);
+}
+
+/* Deallocates *BUFFER (if it was heap-allocated).  */
+static inline void
+scratch_buffer_free (struct scratch_buffer *buffer)
+{
+  if (buffer->data != buffer->__space)
+    free (buffer->data);
+}
+
+/* Grows *BUFFER by some arbitrary amount.  The buffer contents is NOT
+   preserved.  Returns true on success, fails on allocation failure
+   (in which case the old buffer is freed).  On success, the new
+   buffer is slightly larger (by at least 16 bytes) than the previous
+   size.  On failure, *BUFFER is deallocated, but remains in a
+   free-able state.  */
+bool __libc_scratch_buffer_grow (struct scratch_buffer *buffer);
+libc_hidden_proto (__libc_scratch_buffer_grow)
+
+/* Alias for __libc_scratch_buffer_grow.  */
+static inline bool
+scratch_buffer_grow (struct scratch_buffer *buffer)
+{
+  return __glibc_likely (__libc_scratch_buffer_grow (buffer));
+}
+
+/* Like __libc_scratch_buffer_grow, but preserves the old buffer
+   contents on success, as a prefix of the new buffer.  */
+bool __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer);
+libc_hidden_proto (__libc_scratch_buffer_grow_preserve)
+
+/* Alias for __libc_scratch_buffer_grow_preserve.  */
+static inline bool
+scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
+{
+  return __glibc_likely (__libc_scratch_buffer_grow_preserve (buffer));
+}
+
+/* Grows *BUFFER so that it can store at least NELEM elemnts of SIZE
+   bytes.  The buffer contents is NOT preserved.  Returns true on
+   success, fails on allocation failure (in which case the old buffer
+   is freed, but *BUFFER remains a free-able state).  */
+bool __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+					   size_t nelem, size_t size);
+libc_hidden_proto (__libc_scratch_buffer_set_array_size)
+
+/* Alias for __libc_scratch_set_array_size.  */
+static inline bool
+scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+			       size_t nelem, size_t size)
+{
+  return __glibc_likely (__libc_scratch_buffer_set_array_size
+			 (buffer, nelem, size));
+}
diff --git a/malloc/Makefile b/malloc/Makefile
index 5f68a79..9e7112a 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -27,10 +27,12 @@  headers := $(dist-headers) obstack.h mcheck.h
 tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \
 	 tst-malloc-usable tst-realloc tst-posix_memalign \
-	 tst-pvalloc tst-memalign tst-mallopt
+	 tst-pvalloc tst-memalign tst-mallopt tst-scratch_buffer
 test-srcs = tst-mtrace
 
-routines = malloc morecore mcheck mtrace obstack
+routines = malloc morecore mcheck mtrace obstack \
+  scratch_buffer_grow scratch_buffer_grow_preserve \
+  scratch_buffer_set_array_size
 
 install-lib := libmcheck.a
 non-lib.a := libmcheck.a
diff --git a/malloc/Versions b/malloc/Versions
index 7ca9bdf..a88dbe6 100644
--- a/malloc/Versions
+++ b/malloc/Versions
@@ -61,6 +61,11 @@  libc {
   GLIBC_2.16 {
     aligned_alloc;
   }
+  GLIBC_2.22 {
+    __libc_scratch_buffer_grow;
+    __libc_scratch_buffer_grow_preserve;
+    __libc_scratch_buffer_set_array_size;
+  }
   GLIBC_PRIVATE {
     # Internal startup hook for libpthread.
     __libc_malloc_pthread_startup;
diff --git a/malloc/scratch_buffer_grow.c b/malloc/scratch_buffer_grow.c
new file mode 100644
index 0000000..6621393
--- /dev/null
+++ b/malloc/scratch_buffer_grow.c
@@ -0,0 +1,52 @@ 
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <scratch_buffer.h>
+#include <errno.h>
+
+bool
+__libc_scratch_buffer_grow (struct scratch_buffer *buffer)
+{
+  size_t new_length = buffer->length * 2;
+
+  /* Discard old buffer.  */
+  scratch_buffer_free (buffer);
+
+  /* Check for overflow.  */
+  if (__glibc_unlikely (new_length < buffer->length))
+    {
+      /* Buffer must remain valid to free.  */
+      scratch_buffer_init (buffer);
+      __set_errno (ENOMEM);
+      return false;
+    }
+
+  void *new_ptr = malloc (new_length);
+  if (new_ptr == NULL)
+    {
+      /* Buffer must remain valid to free.  */
+      scratch_buffer_init (buffer);
+      return false;
+    }
+
+  /* Install new heap-based buffer.  */
+  buffer->data = new_ptr;
+  buffer->length = new_length;
+  return true;
+}
+libc_hidden_def (__libc_scratch_buffer_grow);
diff --git a/malloc/scratch_buffer_grow_preserve.c b/malloc/scratch_buffer_grow_preserve.c
new file mode 100644
index 0000000..3abbd56
--- /dev/null
+++ b/malloc/scratch_buffer_grow_preserve.c
@@ -0,0 +1,65 @@ 
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <scratch_buffer.h>
+#include <errno.h>
+
+bool
+__libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
+{
+  size_t new_length = 2 * buffer->length;
+  void *new_ptr;
+
+  if (buffer->data == buffer->__space)
+    {
+      /* Move buffer to the heap.  No overflow is possible because
+	 buffer->length describes a small buffer on the stack.  We use
+	 buffer->length instead of sizeof (buffer->__space) to avoid
+	 making the size of struct scratch_buffer part of the ABI. */
+      new_ptr = malloc (new_length);
+      if (new_ptr == NULL)
+	return false;
+      memcpy (new_ptr, buffer->__space, buffer->length);
+    }
+  else
+    {
+      /* Buffer was already on the heap.  Check for overflow.  */
+      if (__glibc_unlikely (new_length < buffer->length))
+	{
+	  /* Deallocate, but buffer must remain valid to free.  */
+	  free (buffer->data);
+	  scratch_buffer_init (buffer);
+	  __set_errno (ENOMEM);
+	  return false;
+	}
+      new_ptr = realloc (buffer->data, new_length);
+      if (new_ptr == NULL)
+	{
+	  /* Deallocate, but buffer must remain valid to free.  */
+	  free (buffer->data);
+	  scratch_buffer_init (buffer);
+	  return false;
+	}
+    }
+
+  /* Install new heap-based buffer.  */
+  buffer->data = new_ptr;
+  buffer->length = new_length;
+  return true;
+}
+libc_hidden_def (__libc_scratch_buffer_grow_preserve);
diff --git a/malloc/scratch_buffer_set_array_size.c b/malloc/scratch_buffer_set_array_size.c
new file mode 100644
index 0000000..7786baf
--- /dev/null
+++ b/malloc/scratch_buffer_set_array_size.c
@@ -0,0 +1,61 @@ 
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <scratch_buffer.h>
+#include <errno.h>
+
+bool
+__libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+				      size_t nelem, size_t size)
+{
+  size_t size_max_square_root = ((size_t)1) << (sizeof (size_t) * 4);
+  /* Avoid overflow check if both values are small. */
+  if (nelem >= size_max_square_root || size >= size_max_square_root)
+    {
+      if (nelem != 0 && size > SIZE_MAX / nelem)
+	{
+	  /* Discard the old buffer, but it must remain valid to
+	     free.  */
+	  scratch_buffer_free (buffer);
+	  scratch_buffer_init (buffer);
+	  __set_errno (ENOMEM);
+	  return false;
+	}
+    }
+
+  size_t new_length = nelem * size;
+  if (new_length <= buffer->length)
+    return true;
+
+  /* Discard old buffer.  */
+  scratch_buffer_free (buffer);
+
+  char *new_ptr = malloc (new_length);
+  if (new_ptr == NULL)
+    {
+      /* Buffer must remain valid to free.  */
+      scratch_buffer_init (buffer);
+      return false;
+    }
+
+  /* Install new heap-based buffer.  */
+  buffer->data = new_ptr;
+  buffer->length = new_length;
+  return true;
+}
+libc_hidden_def (__libc_scratch_buffer_set_array_size);
diff --git a/malloc/tst-scratch_buffer.c b/malloc/tst-scratch_buffer.c
new file mode 100644
index 0000000..dcae512
--- /dev/null
+++ b/malloc/tst-scratch_buffer.c
@@ -0,0 +1,155 @@ 
+/*
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <scratch_buffer.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+static bool
+unchanged_array_size (struct scratch_buffer *buf, size_t a, size_t b)
+{
+  size_t old_length = buf->length;
+  if (!scratch_buffer_set_array_size (buf, a, b))
+    {
+      printf ("scratch_buffer_set_array_size failed: %zu %zu\n",
+	      a, b);
+      return false;
+    }
+  if (old_length != buf->length)
+    {
+      printf ("scratch_buffer_set_array_size did not preserve size: %zu %zu\n",
+	      a, b);
+      return false;
+    }
+  return true;
+}
+
+static bool
+array_size_must_fail (size_t a, size_t b)
+{
+  for (int pass = 0; pass < 2; ++pass)
+    {
+      struct scratch_buffer buf;
+      scratch_buffer_init (&buf);
+      if (pass > 0)
+	if (!scratch_buffer_grow (&buf))
+	  {
+	    printf ("scratch_buffer_grow in array_size_must_fail failed\n");
+	    return false;
+	  }
+      if (scratch_buffer_set_array_size (&buf, a, b))
+	{
+	  printf ("scratch_buffer_set_array_size passed: %d %zu %zu\n",
+		  pass, a, b);
+	  return false;
+	}
+      if (buf.data != buf.__space)
+	{
+	  printf ("scratch_buffer_set_array_size did not free: %d %zu %zu\n",
+		  pass, a, b);
+	  return false;
+	}
+    }
+  return true;
+}
+
+static int
+do_test (void)
+{
+  {
+    struct scratch_buffer buf;
+    scratch_buffer_init (&buf);
+    memset (buf.data, ' ', buf.length);
+    scratch_buffer_free (&buf);
+  }
+  {
+    struct scratch_buffer buf;
+    scratch_buffer_init (&buf);
+    memset (buf.data, ' ', buf.length);
+    size_t old_length = buf.length;
+    scratch_buffer_grow (&buf);
+    if (buf.length <= old_length)
+      {
+	printf ("scratch_buffer_grow did not enlarge buffer\n");
+	return 1;
+      }
+    memset (buf.data, ' ', buf.length);
+    scratch_buffer_free (&buf);
+  }
+  {
+    struct scratch_buffer buf;
+    scratch_buffer_init (&buf);
+    memset (buf.data, '@', buf.length);
+    strcpy (buf.data, "prefix");
+    size_t old_length = buf.length;
+    scratch_buffer_grow_preserve (&buf);
+    if (buf.length <= old_length)
+      {
+	printf ("scratch_buffer_grow_preserve did not enlarge buffer\n");
+	return 1;
+      }
+    if (strcmp (buf.data, "prefix") != 0)
+      {
+	printf ("scratch_buffer_grow_preserve did not copy buffer\n");
+	return 1;
+      }
+    for (unsigned i = 7; i < old_length; ++i)
+      if (((char *)buf.data)[i] != '@')
+	{
+	  printf ("scratch_buffer_grow_preserve did not copy buffer (%u)\n",
+		  i);
+	  return 1;
+	}
+    scratch_buffer_free (&buf);
+  }
+  {
+    struct scratch_buffer buf;
+    scratch_buffer_init (&buf);
+    for (int pass = 0; pass < 4; ++pass)
+      {
+	if (!(unchanged_array_size (&buf, 0, 0)
+	      && unchanged_array_size (&buf, 1, 0)
+	      && unchanged_array_size (&buf, 0, 1)
+	      && unchanged_array_size (&buf, -1, 0)
+	      && unchanged_array_size (&buf, 0, -1)
+	      && unchanged_array_size (&buf, 1ULL << 16, 0)
+	      && unchanged_array_size (&buf, 0, 1ULL << 16)
+	      && unchanged_array_size (&buf, 1ULL << 32, 0)
+	      && unchanged_array_size (&buf, 0, 1ULL << 32)))
+	  return 1;
+	if (!scratch_buffer_grow (&buf))
+	  {
+	    printf ("scratch_buffer_grow_failed (pass %d)\n", pass);
+	  }
+      }
+    scratch_buffer_free (&buf);
+  }
+  {
+    if (!(array_size_must_fail (-1, 1)
+	  && array_size_must_fail (-1, -1)
+	  && array_size_must_fail (1, -1)
+	  && array_size_must_fail (((size_t)-1) / 4, 4)
+	  && array_size_must_fail (4, ((size_t)-1) / 4)))
+	return 1;
+  }
+  return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index aeee312..c997185 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2081,3 +2081,8 @@  GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
  _mcount F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 980e088..ece9220 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2012,6 +2012,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index ce45208..aee7e03 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -89,6 +89,11 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.4
  GLIBC_2.4 A
  _Exit F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 58407fc..fbc040e 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1860,6 +1860,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 3cb314d..98f97e1 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2023,6 +2023,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 067552d..ceb4cd4 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1881,6 +1881,11 @@  GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
  getunwind F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index f06cc8e..cb36531 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -90,6 +90,11 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.4
  GLIBC_2.4 A
  _Exit F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 9010ea7..314bd55 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1979,6 +1979,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 6e8d993..1cd28be 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2080,3 +2080,8 @@  GLIBC_2.18
  xencrypt F
  xprt_register F
  xprt_unregister F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 1c3490c..a40707e 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1951,6 +1951,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index d8fd823..bf0d583 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1949,6 +1949,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 3e6ed35..1faf5ce 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1947,6 +1947,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index c7e46aa..4394069 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1941,6 +1941,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 0703418..77ea7a9 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2121,3 +2121,8 @@  GLIBC_2.21
  xencrypt F
  xprt_register F
  xprt_unregister F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index f27b48b..8eba8ff 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1983,6 +1983,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index a54382e..c7df1e0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1989,6 +1989,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 195b587..f7c5188 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -90,6 +90,11 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  _Exit F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 0194f0b..3ca48ce 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1984,6 +1984,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 807f702..23951bb 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1880,6 +1880,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index a653292..47eaa7b 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1864,6 +1864,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 9defbdf..09cb658 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1975,6 +1975,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 35987fa..ea21091 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1908,6 +1908,11 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index caf74b8..069595f 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2091,3 +2091,8 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
index 68d975b..c1343b8 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2091,3 +2091,8 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index caf74b8..069595f 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2091,3 +2091,8 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 914b590..2c15f91 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1854,6 +1854,11 @@  GLIBC_2.2.5
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 0f64c8d..7c9c5dc 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2089,3 +2089,8 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.22
+ GLIBC_2.22 A
+ __libc_scratch_buffer_grow F
+ __libc_scratch_buffer_grow_preserve F
+ __libc_scratch_buffer_set_array_size F