History

mm, sl[aou]b: guarantee natural alignment for kmalloc(power-of-two)

In most configurations, kmalloc() happens to return naturally aligned (i.e. aligned to the block size itself) blocks for power of two sizes. That means some kmalloc() users might unknowingly rely on that alignment, until stuff breaks when the kernel is built with e.g. CONFIG_SLUB_DEBUG or CONFIG_SLOB, and blocks stop being aligned. Then developers have to devise workaround such as own kmem caches with specified alignment [1], which is not always practical, as recently evidenced in [2]. The topic has been discussed at LSF/MM 2019 [3]. Adding a 'kmalloc_aligned()' variant would not help with code unknowingly relying on the implicit alignment. For slab implementations it would either require creating more kmalloc caches, or allocate a larger size and only give back part of it. That would be wasteful, especially with a generic alignment parameter (in contrast with a fixed alignment to size). Ideally we should provide to mm users what they need without difficult workarounds or own reimplementations, so let's make the kmalloc() alignment to size explicitly guaranteed for power-of-two sizes under all configurations. What this means for the three available allocators? * SLAB object layout happens to be mostly unchanged by the patch. The implicitly provided alignment could be compromised with CONFIG_DEBUG_SLAB due to redzoning, however SLAB disables redzoning for caches with alignment larger than unsigned long long. Practically on at least x86 this includes kmalloc caches as they use cache line alignment, which is larger than that. Still, this patch ensures alignment on all arches and cache sizes. * SLUB layout is also unchanged unless redzoning is enabled through CONFIG_SLUB_DEBUG and boot parameter for the particular kmalloc cache. With this patch, explicit alignment is guaranteed with redzoning as well. This will result in more memory being wasted, but that should be acceptable in a debugging scenario. * SLOB has no implicit alignment so this patch adds it explicitly for kmalloc(). The potential downside is increased fragmentation. While pathological allocation scenarios are certainly possible, in my testing, after booting a x86_64 kernel+userspace with virtme, around 16MB memory was consumed by slab pages both before and after the patch, with difference in the noise. [1] https://lore.kernel.org/linux-btrfs/c3157c8e8e0e7588312b40c853f65c02fe6c957a.1566399731.git.christophe.leroy@c-s.fr/ [2] https://lore.kernel.org/linux-fsdevel/20190225040904.5557-1-ming.lei@redhat.com/ [3] https://lwn.net/Articles/787740/ [akpm@linux-foundation.org: documentation fixlet, per Matthew] Link: http://lkml.kernel.org/r/20190826111627.7505-3-vbabka@suse.cz Signed-off-by: Vlastimil Babka <vbabka@suse.cz> Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org> Acked-by: Michal Hocko <mhocko@suse.com> Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Acked-by: Christoph Hellwig <hch@lst.de> Cc: David Sterba <dsterba@suse.cz> Cc: Christoph Lameter <cl@linux.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Ming Lei <ming.lei@redhat.com> Cc: Dave Chinner <david@fromorbit.com> Cc: "Darrick J . Wong" <darrick.wong@oracle.com> Cc: Christoph Hellwig <hch@lst.de> Cc: James Bottomley <James.Bottomley@HansenPartnership.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Vlastimil Babka committed 7 years ago

Commit 59bb4791 parent 6a486c04 files · +49 −12Browse files

Unified · Split

4 files
  1. Documentation/core-api/memory-allocation.rst +4 −0
  2. include/linux/slab.h +4 −0
  3. mm/slab_common.c +10 −1
  4. mm/slob.c +31 −11
Documentation/core-api/memory-allocation.rst+4−0
@@ -98,6 +98,10 @@ limited. The actual limit depends on the hardware and the kernel · 97 unmodified lines
9898 configuration, but it is a good practice to use `kmalloc` for objects
9999 smaller than page size.
100100
101+The address of a chunk allocated with `kmalloc` is aligned to at least
102+ARCH_KMALLOC_MINALIGN bytes. For sizes which are a power of two, the
103+alignment is also guaranteed to be at least the respective size.
104+
101105 For large allocations you can use :c:func:`vmalloc` and
102106 :c:func:`vzalloc`, or directly request pages from the page
103107 allocator. The memory allocated by `vmalloc` and related functions is
include/linux/slab.h+4−0
@@ -493,6 +493,10 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags) · 492 unmodified lines
493493 * kmalloc is the normal method of allocating memory
494494 * for objects smaller than page size in the kernel.
495495 *
496+ * The allocated object address is aligned to at least ARCH_KMALLOC_MINALIGN
497+ * bytes. For @size of power of two bytes, the alignment is also guaranteed
498+ * to be at least to the size.
499+ *
496500 * The @flags argument may be one of the GFP flags defined at
497501 * include/linux/gfp.h and described at
498502 * :ref:`Documentation/core-api/mm-api.rst <mm-api-gfp-flags>`
mm/slab_common.c+10−1
@@ -1030,10 +1030,19 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name, · 1029 unmodified lines
10301030 unsigned int useroffset, unsigned int usersize)
10311031 {
10321032 int err;
1033+ unsigned int align = ARCH_KMALLOC_MINALIGN;
10331034
10341035 s->name = name;
10351036 s->size = s->object_size = size;
1036− s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size);
1037+
1038+ /*
1039+ * For power of two sizes, guarantee natural alignment for kmalloc
1040+ * caches, regardless of SL*B debugging options.
1041+ */
1042+ if (is_power_of_2(size))
1043+ align = max(align, size);
1044+ s->align = calculate_alignment(flags, align, size);
1045+
10371046 s->useroffset = useroffset;
10381047 s->usersize = usersize;
10391048
mm/slob.c+31−11
@@ -224,6 +224,7 @@ static void slob_free_pages(void *b, int order) · 223 unmodified lines
224224 * @sp: Page to look in.
225225 * @size: Size of the allocation.
226226 * @align: Allocation alignment.
227+ * @align_offset: Offset in the allocated block that will be aligned.
227228 * @page_removed_from_list: Return parameter.
228229 *
229230 * Tries to find a chunk of memory at least @size bytes big within @page.
@@ -234,7 +235,7 @@ static void slob_free_pages(void *b, int order) · 4 unmodified lines
234235 * true (set to false otherwise).
235236 */
236237 static void *slob_page_alloc(struct page *sp, size_t size, int align,
237− bool *page_removed_from_list)
238+ int align_offset, bool *page_removed_from_list)
238239 {
239240 slob_t *prev, *cur, *aligned = NULL;
240241 int delta = 0, units = SLOB_UNITS(size);
@@ -243,8 +244,17 @@ static void *slob_page_alloc(struct page *sp, size_t size, int align, · 2 unmodified lines
243244 for (prev = NULL, cur = sp->freelist; ; prev = cur, cur = slob_next(cur)) {
244245 slobidx_t avail = slob_units(cur);
245246
247+ /*
248+ * 'aligned' will hold the address of the slob block so that the
249+ * address 'aligned'+'align_offset' is aligned according to the
250+ * 'align' parameter. This is for kmalloc() which prepends the
251+ * allocated block with its size, so that the block itself is
252+ * aligned when needed.
253+ */
246254 if (align) {
247− aligned = (slob_t *)ALIGN((unsigned long)cur, align);
255+ aligned = (slob_t *)
256+ (ALIGN((unsigned long)cur + align_offset, align)
257+ - align_offset);
248258 delta = aligned - cur;
249259 }
250260 if (avail >= units + delta) { /* room enough? */
@@ -288,7 +298,8 @@ static void *slob_page_alloc(struct page *sp, size_t size, int align, · 37 unmodified lines
288298 /*
289299 * slob_alloc: entry point into the slob allocator.
290300 */
291−static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
301+static void *slob_alloc(size_t size, gfp_t gfp, int align, int node,
302+ int align_offset)
292303 {
293304 struct page *sp;
294305 struct list_head *slob_list;
@@ -319,7 +330,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) · 24 unmodified lines
319330 if (sp->units < SLOB_UNITS(size))
320331 continue;
321332
322− b = slob_page_alloc(sp, size, align, &page_removed_from_list);
333+ b = slob_page_alloc(sp, size, align, align_offset, &page_removed_from_list);
323334 if (!b)
324335 continue;
325336
@@ -356,7 +367,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) · 30 unmodified lines
356367 INIT_LIST_HEAD(&sp->slab_list);
357368 set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));
358369 set_slob_page_free(sp, slob_list);
359− b = slob_page_alloc(sp, size, align, &_unused);
370+ b = slob_page_alloc(sp, size, align, align_offset, &_unused);
360371 BUG_ON(!b);
361372 spin_unlock_irqrestore(&slob_lock, flags);
362373 }
@@ -458,7 +469,7 @@ static __always_inline void * · 95 unmodified lines
458469 __do_kmalloc_node(size_t size, gfp_t gfp, int node, unsigned long caller)
459470 {
460471 unsigned int *m;
461− int align = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN);
472+ int minalign = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN);
462473 void *ret;
463474
464475 gfp &= gfp_allowed_mask;
@@ -466,19 +477,28 @@ __do_kmalloc_node(size_t size, gfp_t gfp, int node, unsigned long caller) · 1 unmodified line
466477 fs_reclaim_acquire(gfp);
467478 fs_reclaim_release(gfp);
468479
469− if (size < PAGE_SIZE - align) {
480+ if (size < PAGE_SIZE - minalign) {
481+ int align = minalign;
482+
483+ /*
484+ * For power of two sizes, guarantee natural alignment for
485+ * kmalloc()'d objects.
486+ */
487+ if (is_power_of_2(size))
488+ align = max(minalign, (int) size);
489+
470490 if (!size)
471491 return ZERO_SIZE_PTR;
472492
473− m = slob_alloc(size + align, gfp, align, node);
493+ m = slob_alloc(size + minalign, gfp, align, node, minalign);
474494
475495 if (!m)
476496 return NULL;
477497 *m = size;
478− ret = (void *)m + align;
498+ ret = (void *)m + minalign;
479499
480500 trace_kmalloc_node(caller, ret,
481− size, size + align, gfp, node);
501+ size, size + minalign, gfp, node);
482502 } else {
483503 unsigned int order = get_order(size);
484504
@@ -579,7 +599,7 @@ static void *slob_alloc_node(struct kmem_cache *c, gfp_t flags, int node) · 94 unmodified lines
579599 fs_reclaim_release(flags);
580600
581601 if (c->size < PAGE_SIZE) {
582− b = slob_alloc(c->size, flags, c->align, node);
602+ b = slob_alloc(c->size, flags, c->align, node, 0);
583603 trace_kmem_cache_alloc_node(_RET_IP_, b, c->object_size,
584604 SLOB_UNITS(c->size) * SLOB_UNIT,
585605 flags, node);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t