summaryrefslogtreecommitdiff
path: root/common/dlmalloc.c
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2016-04-25 15:55:42 -0600
committerTom Rini <trini@konsulko.com>2016-05-23 11:50:17 -0400
commit034eda867f47986e04be13087d193d2c12e3b9aa (patch)
tree498850a7a94a4818254785998b77a2cf2bf4b5e2 /common/dlmalloc.c
parent83b6a43e6efdce32c7f824263ad40c86d03901f9 (diff)
malloc: improve memalign fragmentation fix
Commit 4f144a416469 "malloc: work around some memalign fragmentation issues" enhanced memalign() so that it can succeed in more cases where heap fragmentation is present. However, it did not solve as many cases as it could. This patch enhances the code to cover more cases. The alignment code works by allocating more space than the user requests, then adjusting the returned pointer to achieve alignment. In general, one must allocate "alignment" bytes more than the user requested in order to guarantee that alignment is possible. This is what the original code does. The previous enhancement attempted a second allocation if the padded allocation failed, and succeeded if that allocation just happened to be aligned; a fluke that happened often in practice. There are still cases where this could fail, yet where it is still possible to honor the user's allocation request. In particular, if the heap contains a free region that is large enough for the user's request, and for leading padding to ensure alignment, but has no or little space for any trailing padding. In this case, we can make a third(!) allocation attempt after calculating exactly the size of the leading padding required to achieve alignment, which is the minimal over-allocation needed for the overall memalign() operation to succeed if the third and second allocations end up at the same location. This patch isn't checkpatch-clean, since it conforms to the existing coding style in dlmalloc.c, which is different to the rest of U-Boot. Signed-off-by: Stephen Warren <swarren@nvidia.com> Reviewed-by: Tom Rini <trini@konsulko.com>
Diffstat (limited to 'common/dlmalloc.c')
-rw-r--r--common/dlmalloc.c23
1 files changed, 21 insertions, 2 deletions
diff --git a/common/dlmalloc.c b/common/dlmalloc.c
index b09f5249a90..adc680e9593 100644
--- a/common/dlmalloc.c
+++ b/common/dlmalloc.c
@@ -1909,6 +1909,7 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
* fulfill the user's request.
*/
if (m == NULL) {
+ size_t extra, extra2;
/*
* Use bytes not nb, since mALLOc internally calls request2size too, and
* each call increases the size to allocate, to account for the header.
@@ -1917,9 +1918,27 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
/* Aligned -> return it */
if ((((unsigned long)(m)) % alignment) == 0)
return m;
- /* Otherwise, fail */
+ /*
+ * Otherwise, try again, requesting enough extra space to be able to
+ * acquire alignment.
+ */
fREe(m);
- m = NULL;
+ /* Add in extra bytes to match misalignment of unexpanded allocation */
+ extra = alignment - (((unsigned long)(m)) % alignment);
+ m = (char*)(mALLOc(bytes + extra));
+ /*
+ * m might not be the same as before. Validate that the previous value of
+ * extra still works for the current value of m.
+ * If (!m), extra2=alignment so
+ */
+ if (m) {
+ extra2 = alignment - (((unsigned long)(m)) % alignment);
+ if (extra2 > extra) {
+ fREe(m);
+ m = NULL;
+ }
+ }
+ /* Fall through to original NULL check and chunk splitting logic */
}
if (m == NULL) return NULL; /* propagate failure */