From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:19:36
Hello everyone,
There have been a bit quiet about Contiguous Memory Allocator for last
months. This is mainly caused by the fact that Michal (the author of CMA
patches) has left our team and we needed some time to takeover the
development.
This version is mainly a rebase and adaptation for 2.6.39-rc1 kernel
release. I've also managed to fix a bunch of nasty bugs that caused
problems if the allocation failed.
A few words for these who see CMA for the first time:
The Contiguous Memory Allocator (CMA) makes it possible for device
drivers to allocate big contiguous chunks of memory after the system
has booted.
The main difference from the similar frameworks is the fact that CMA
allows to transparently reuse memory region reserved for the big
chunk allocation as a system memory, so no memory is wasted when no
big chunk is allocated. Once the alloc request is issued, the
framework will migrate system pages to create a required big chunk of
physically contiguous memory.
For more information see the changelog and links to previous versions
of CMA framework.
The current version is just an allocator that handles allocation of
contiguous memory blocks. The difference between this patchset and
Kamezawa's alloc_contig_pages() are:
1. alloc_contig_pages() requires MAX_ORDER alignment of allocations
which may be unsuitable for embeded systems where a few MiBs are
required.
Lack of the requirement on the alignment means that several threads
might try to access the same pageblock/page. To prevent this from
happening CMA uses a mutex so that only one cm_alloc()/cm_free()
function may run at one point.
2. CMA may use its own migratetype (MIGRATE_CMA) which behaves
similarly to ZONE_MOVABLE but can be put in arbitrary places.
This is required for us since we need to define two disjoint memory
ranges inside system RAM. (ie. in two memory banks (do not confuse
with nodes)).
3. alloc_contig_pages() scans memory in search for range that could be
migrated. CMA on the other hand maintains its own allocator to
decide where to allocate memory for device drivers and then tries
to migrate pages from that part if needed. This is not strictly
required but I somehow feel it might be faster.
The current version doesn't handle any DMA mapping for the allocated
chunks. This topic will be handled by a separate patch series in the
future.
Despite that, we were able to test this version with real multimedia
hardware on Samsung S5PC110 platform. We used V4L2 drivers that already
contain support for CMA-based memory allocator in videobuf2 framework.
Please refer to last 3 patches of this series to get the idea how it can
be used.
Links to previous versions of the patchset:
v8: <http://article.gmane.org/gmane.linux.kernel.mm/56855>
v7: <http://article.gmane.org/gmane.linux.kernel.mm/55626>
v6: <http://article.gmane.org/gmane.linux.kernel.mm/55626>
v5: (intentionally left out as CMA v5 was identical to CMA v4)
v4: <http://article.gmane.org/gmane.linux.kernel.mm/52010>
v3: <http://article.gmane.org/gmane.linux.kernel.mm/51573>
v2: <http://article.gmane.org/gmane.linux.kernel.mm/50986>
v1: <http://article.gmane.org/gmane.linux.kernel.mm/50669>
Changelog:
v9: 1. Rebased onto 2.6.39-rc1 and resolved all conflicts
2. Fixed a bunch of nasty bugs that happened when the allocation
failed (mainly kernel oops due to NULL ptr dereference).
3. Introduced testing code: cma-regions compatibility layer and
videobuf2-cma memory allocator module.
v8: 1. The alloc_contig_range() function has now been separated from
CMA and put in page_allocator.c. This function tries to
migrate all LRU pages in specified range and then allocate the
range using alloc_contig_freed_pages().
2. Support for MIGRATE_CMA has been separated from the CMA code.
I have not tested if CMA works with ZONE_MOVABLE but I see no
reasons why it shouldn't.
3. I have added a @private argument when creating CMA contexts so
that one can reserve memory and not share it with the rest of
the system. This way, CMA acts only as allocation algorithm.
v7: 1. A lot of functionality that handled driver->allocator_context
mapping has been removed from the patchset. This is not to say
that this code is not needed, it's just not worth posting
everything in one patchset.
Currently, CMA is "just" an allocator. It uses it's own
migratetype (MIGRATE_CMA) for defining ranges of pageblokcs
which behave just like ZONE_MOVABLE but dispite the latter can
be put in arbitrary places.
2. The migration code that was introduced in the previous version
actually started working.
v6: 1. Most importantly, v6 introduces support for memory migration.
The implementation is not yet complete though.
Migration support means that when CMA is not using memory
reserved for it, page allocator can allocate pages from it.
When CMA wants to use the memory, the pages have to be moved
and/or evicted as to make room for CMA.
To make it possible it must be guaranteed that only movable and
reclaimable pages are allocated in CMA controlled regions.
This is done by introducing a MIGRATE_CMA migrate type that
guarantees exactly that.
Some of the migration code is "borrowed" from Kamezawa
Hiroyuki's alloc_contig_pages() implementation. The main
difference is that thanks to MIGRATE_CMA migrate type CMA
assumes that memory controlled by CMA are is always movable or
reclaimable so that it makes allocation decisions regardless of
the whether some pages are actually allocated and migrates them
if needed.
The most interesting patches from the patchset that implement
the functionality are:
09/13: mm: alloc_contig_free_pages() added
10/13: mm: MIGRATE_CMA migration type added
11/13: mm: MIGRATE_CMA isolation functions added
12/13: mm: cma: Migration support added [wip]
Currently, kernel panics in some situations which I am trying
to investigate.
2. cma_pin() and cma_unpin() functions has been added (after
a conversation with Johan Mossberg). The idea is that whenever
hardware does not use the memory (no transaction is on) the
chunk can be moved around. This would allow defragmentation to
be implemented if desired. No defragmentation algorithm is
provided at this time.
3. Sysfs support has been replaced with debugfs. I always felt
unsure about the sysfs interface and when Greg KH pointed it
out I finally got to rewrite it to debugfs.
v5: (intentionally left out as CMA v5 was identical to CMA v4)
v4: 1. The "asterisk" flag has been removed in favour of requiring
that platform will provide a "*=<regions>" rule in the map
attribute.
2. The terminology has been changed slightly renaming "kind" to
"type" of memory. In the previous revisions, the documentation
indicated that device drivers define memory kinds and now,
v3: 1. The command line parameters have been removed (and moved to
a separate patch, the fourth one). As a consequence, the
cma_set_defaults() function has been changed -- it no longer
accepts a string with list of regions but an array of regions.
2. The "asterisk" attribute has been removed. Now, each region
has an "asterisk" flag which lets one specify whether this
region should by considered "asterisk" region.
3. SysFS support has been moved to a separate patch (the third one
in the series) and now also includes list of regions.
v2: 1. The "cma_map" command line have been removed. In exchange,
a SysFS entry has been created under kernel/mm/contiguous.
The intended way of specifying the attributes is
a cma_set_defaults() function called by platform initialisation
code. "regions" attribute (the string specified by "cma"
command line parameter) can be overwritten with command line
parameter; the other attributes can be changed during run-time
using the SysFS entries.
2. The behaviour of the "map" attribute has been modified
slightly. Currently, if no rule matches given device it is
assigned regions specified by the "asterisk" attribute. It is
by default built from the region names given in "regions"
attribute.
3. Devices can register private regions as well as regions that
can be shared but are not reserved using standard CMA
mechanisms. A private region has no name and can be accessed
only by devices that have the pointer to it.
4. The way allocators are registered has changed. Currently,
a cma_allocator_register() function is used for that purpose.
Moreover, allocators are attached to regions the first time
memory is registered from the region or when allocator is
registered which means that allocators can be dynamic modules
that are loaded after the kernel booted (of course, it won't be
possible to allocate a chunk of memory from a region if
allocator is not loaded).
5. Index of new functions:
+static inline dma_addr_t __must_check
+cma_alloc_from(const char *regions, size_t size,
+ dma_addr_t alignment)
+static inline int
+cma_info_about(struct cma_info *info, const const char *regions)
+int __must_check cma_region_register(struct cma_region *reg);
+dma_addr_t __must_check
+cma_alloc_from_region(struct cma_region *reg,
+ size_t size, dma_addr_t alignment);
+static inline dma_addr_t __must_check
+cma_alloc_from(const char *regions,
+ size_t size, dma_addr_t alignment);
+int cma_allocator_register(struct cma_allocator *alloc);
Patches in this patchset:
lib: bitmap: Added alignment offset for bitmap_find_next_zero_area()
lib: genalloc: Generic allocator improvements
Some improvements to genalloc API (most importantly possibility to
allocate memory with alignment requirement).
mm: move some functions from memory_hotplug.c to page_isolation.c
mm: alloc_contig_freed_pages() added
Code "stolen" from Kamezawa. The first patch just moves code
around and the second provide function for "allocates" already
freed memory.
mm: alloc_contig_range() added
This is what Kamezawa asked: a function that tries to migrate all
pages from given range and then use alloc_contig_freed_pages()
(defined by the previous commit) to allocate those pages.
mm: cma: Contiguous Memory Allocator added
The CMA code but with no MIGRATE_CMA support yet. This assues
that one uses a ZONE_MOVABLE.
mm: MIGRATE_CMA migration type added
mm: MIGRATE_CMA isolation functions added
mm: MIGRATE_CMA support added to CMA
Introduction of the new migratetype and support for it in CMA.
MIGRATE_CMA works similar to ZONE_MOVABLE expect almost any
memory range can be marked as one.
mm: cma: add CMA 'regions style' API (for testing)
This is a compatiblity layer with older CMA v1 API. It is mostly
intended to test the CMA framework with multimedia drivers
that we have available for Samsung S5PC110 platform. Not for
merging, just an example.
v4l: videobuf2: add CMA allocator (for testing)
Main client of CMA 'region style' framework. Updated to latest
changes in cma and cma-regions API. Used for testing multimedia
drivers that we have available for Samsung S5PC110 platform.
Not for merging, just an example.
ARM: S5PC110: Added CMA regions to Aquila and Goni boards
A stub integration with some ARM machines. Mostly to get the cma
testing device working. Not for merging, just an example.
Patch summary:
KAMEZAWA Hiroyuki (2):
mm: move some functions from memory_hotplug.c to page_isolation.c
mm: alloc_contig_freed_pages() added
Marek Szyprowski (3):
mm: cma: add CMA 'regions style' API (for testing)
v4l: videobuf2: add CMA allocator (for testing)
ARM: cma: Added CMA regions to Aquila and Goni boards
Michal Nazarewicz (7):
lib: bitmap: Added alignment offset for bitmap_find_next_zero_area()
lib: genalloc: Generic allocator improvements
mm: alloc_contig_range() added
mm: cma: Contiguous Memory Allocator added
mm: MIGRATE_CMA migration type added
mm: MIGRATE_CMA isolation functions added
mm: MIGRATE_CMA support added to CMA
arch/arm/mach-s5pv210/mach-aquila.c | 31 ++
arch/arm/mach-s5pv210/mach-goni.c | 31 ++
drivers/media/video/Kconfig | 6 +
drivers/media/video/Makefile | 1 +
drivers/media/video/videobuf2-cma.c | 227 +++++++++++
include/linux/bitmap.h | 24 +-
include/linux/cma-regions.h | 340 ++++++++++++++++
include/linux/cma.h | 264 ++++++++++++
include/linux/genalloc.h | 46 ++-
include/linux/mmzone.h | 43 ++-
include/linux/page-isolation.h | 50 ++-
include/media/videobuf2-cma.h | 40 ++
lib/bitmap.c | 22 +-
lib/genalloc.c | 182 +++++----
mm/Kconfig | 34 ++
mm/Makefile | 1 +
mm/cma-regions.c | 759 +++++++++++++++++++++++++++++++++++
mm/cma.c | 457 +++++++++++++++++++++
mm/compaction.c | 10 +
mm/internal.h | 3 +
mm/memory_hotplug.c | 109 -----
mm/page_alloc.c | 292 +++++++++++++-
mm/page_isolation.c | 126 ++++++-
23 files changed, 2827 insertions(+), 271 deletions(-)
create mode 100644 drivers/media/video/videobuf2-cma.c
create mode 100644 include/linux/cma-regions.h
create mode 100644 include/linux/cma.h
create mode 100644 include/media/videobuf2-cma.h
create mode 100644 mm/cma-regions.c
create mode 100644 mm/cma.c
--
1.7.1.569.g6f426
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:16:16
From: KAMEZAWA Hiroyuki <redacted>
This commit introduces alloc_contig_freed_pages() function
which allocates (ie. removes from buddy system) free pages
in range. Caller has to guarantee that all pages in range
are in buddy system.
Along with this function, a free_contig_pages() function is
provided which frees all (or a subset of) pages allocated
with alloc_contig_free_pages().
Michal Nazarewicz has modified the function to make it easier
to allocate not MAX_ORDER_NR_PAGES aligned pages by making it
return pfn of one-past-the-last allocated page.
Signed-off-by: KAMEZAWA Hiroyuki <redacted>
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/page-isolation.h | 3 ++
mm/page_alloc.c | 44 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 0 deletions(-)
@@ -32,6 +32,9 @@ test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn);*/externintset_migratetype_isolate(structpage*page);externvoidunset_migratetype_isolate(structpage*page);+externunsignedlongalloc_contig_freed_pages(unsignedlongstart,+unsignedlongend,gfp_tflag);+externvoidfree_contig_pages(structpage*page,intnr_pages);/**Formigration.
@@ -5545,6 +5545,50 @@ out:spin_unlock_irqrestore(&zone->lock,flags);}+unsignedlongalloc_contig_freed_pages(unsignedlongstart,unsignedlongend,+gfp_tflag)+{+unsignedlongpfn=start,count;+structpage*page;+structzone*zone;+intorder;++VM_BUG_ON(!pfn_valid(start));+zone=page_zone(pfn_to_page(start));++spin_lock_irq(&zone->lock);++page=pfn_to_page(pfn);+for(;;){+VM_BUG_ON(page_count(page)||!PageBuddy(page));+list_del(&page->lru);+order=page_order(page);+zone->free_area[order].nr_free--;+rmv_page_order(page);+__mod_zone_page_state(zone,NR_FREE_PAGES,-(1UL<<order));+pfn+=1<<order;+if(pfn>=end)+break;+VM_BUG_ON(!pfn_valid(pfn));+page+=1<<order;+}++spin_unlock_irq(&zone->lock);++/* After this, pages in the range can be freed one be one */+page=pfn_to_page(start);+for(count=pfn-start;count;--count,++page)+prep_new_page(page,0,flag);++returnpfn;+}++voidfree_contig_pages(structpage*page,intnr_pages)+{+for(;nr_pages;--nr_pages,++page)+__free_page(page);+}+#ifdef CONFIG_MEMORY_HOTREMOVE/**Allpagesintherangemustbeisolatedbeforecallingthis.
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:16:18
From: Michal Nazarewicz <redacted>
The Contiguous Memory Allocator is a set of functions that lets
one initialise a region of memory which then can be used to perform
allocations of contiguous memory chunks from.
CMA allows for creation of private and non-private contexts.
The former is reserved for CMA and no other kernel subsystem can
use it. The latter allows for movable pages to be allocated within
CMA's managed memory so that it can be used for page cache when
CMA devices do not use it.
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/cma.h | 219 ++++++++++++++++++++++++++++++++++
mm/Kconfig | 28 +++++
mm/Makefile | 1 +
mm/cma.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++++++
mm/page_alloc.c | 2 +-
5 files changed, 579 insertions(+), 1 deletions(-)
create mode 100644 include/linux/cma.h
create mode 100644 mm/cma.c
@@ -340,6 +340,34 @@ choicebenefit.endchoice+configCMA+bool"Contiguous Memory Allocator framework"+# Currently there is only one allocator so force it on+selectMIGRATION+selectGENERIC_ALLOCATOR+help+ThisenablestheContiguousMemoryAllocatorframeworkwhich+allowsdriverstoallocatebigphysically-contiguousblocksof+memoryforusewithhardwarecomponentsthatdonotsupportI/O+mapnorscatter-gather.++Ifyouselectthisoptionyouwillalsohavetoselect@least+oneallocatoralgorithmbelow.++TomakeuseofCMAyouneedtospecifytheregionsand+driver->regionmappingoncommandlinewhenbootingthekernel.++Formoreinformationsee<include/linux/cma.h>.Ifunsure,say"n".++configCMA_DEBUG+bool"CMA debug messages (DEVELOPEMENT)"+depends onCMA+help+TurnsondebugmessagesinCMA.ThisproducesKERN_DEBUG+messagesforeveryCMAcallaswellasvariousmessageswhile+processingcallssuchascma_alloc().Thisoptiondoesnot+affectwarninganderrormessages.+## UP and nommu archs use km based percpu allocator#
@@ -0,0 +1,330 @@+/*+*ContiguousMemoryAllocatorframework+*Copyright(c)2010bySamsungElectronics.+*WrittenbyMichalNazarewicz(m.nazarewiczatsamsung.com)+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicenseas+*publishedbytheFreeSoftwareFoundation;eitherversion2ofthe+*Licenseor(atyouroptional)anylaterversionofthelicense.+*/++/*+*Seeinclude/linux/cma.hfordetails.+*/++#define pr_fmt(fmt) "cma: " fmt++#ifdef CONFIG_CMA_DEBUG+# define DEBUG+#endif++#include<linux/cma.h>++#ifndef CONFIG_NO_BOOTMEM+#include<linux/bootmem.h>+#endif+#ifdef CONFIG_HAVE_MEMBLOCK+#include<linux/memblock.h>+#endif++#include<linux/err.h>+#include<linux/genalloc.h>+#include<linux/mm.h>+#include<linux/module.h>+#include<linux/mutex.h>+#include<linux/page-isolation.h>+#include<linux/slab.h>+#include<linux/swap.h>++#include<asm/page.h>++#include"internal.h"++/* XXX Revisit */+#ifdef phys_to_pfn+/* nothing to do */+#elif defined __phys_to_pfn+# define phys_to_pfn __phys_to_pfn+#else+# warning correct phys_to_pfn implementation needed+staticunsignedlongphys_to_pfn(phys_addr_tphys)+{+returnvirt_to_pfn(phys_to_virt(phys));+}+#endif+++/************************* Initialise CMA *************************/++unsignedlongcma_reserve(unsignedlongstart,unsignedlongsize,+unsignedlongalignment)+{+pr_debug("%s(%p+%p/%p)\n",__func__,(void*)start,(void*)size,+(void*)alignment);++/* Sanity checks */+if(!size||(alignment&(alignment-1)))+return(unsignedlong)-EINVAL;++/* Sanitise input arguments */+start=PAGE_ALIGN(start);+size=PAGE_ALIGN(size);+if(alignment<PAGE_SIZE)+alignment=PAGE_SIZE;++/* Reserve memory */+if(start){+if(memblock_is_region_reserved(start,size)||+memblock_reserve(start,size)<0)+return(unsignedlong)-EBUSY;+}else{+/*+*Use__memblock_alloc_base()since+*memblock_alloc_base()panic()s.+*/+u64addr=__memblock_alloc_base(size,alignment,0);+if(!addr){+return(unsignedlong)-ENOMEM;+}elseif(addr+size>~(unsignedlong)0){+memblock_free(addr,size);+return(unsignedlong)-EOVERFLOW;+}else{+start=addr;+}+}++returnstart;+}+++/************************** CMA context ***************************/++structcma{+boolmigrate;+structgen_pool*pool;+};++staticint__cma_check_range(unsignedlongstart,unsignedlongsize)+{+unsignedlongpfn,count;+structpage*page;+structzone*zone;++start=phys_to_pfn(start);+if(WARN_ON(!pfn_valid(start)))+return-EINVAL;++if(WARN_ON(page_zonenum(pfn_to_page(start))!=ZONE_MOVABLE))+return-EINVAL;++/* First check if all pages are valid and in the same zone */+zone=page_zone(pfn_to_page(start));+count=size>>PAGE_SHIFT;+pfn=start;+while(++pfn,--count){+if(WARN_ON(!pfn_valid(pfn))||+WARN_ON(page_zone(pfn_to_page(pfn))!=zone))+return-EINVAL;+}++/* Now check migratetype of their pageblocks. */+start=start&~(pageblock_nr_pages-1);+pfn=ALIGN(pfn,pageblock_nr_pages);+page=pfn_to_page(start);+count=(pfn-start)>>PAGE_SHIFT;+do{+if(WARN_ON(get_pageblock_migratetype(page)!=MIGRATE_MOVABLE))+return-EINVAL;+page+=pageblock_nr_pages;+}while(--count);++return0;+}++structcma*cma_create(unsignedlongstart,unsignedlongsize,+unsignedlongmin_alignment,boolprivate)+{+structgen_pool*pool;+structcma*cma;+intret;++pr_debug("%s(%p+%p)\n",__func__,(void*)start,(void*)size);++if(!size)+returnERR_PTR(-EINVAL);+if(min_alignment&(min_alignment-1))+returnERR_PTR(-EINVAL);+if(min_alignment<PAGE_SIZE)+min_alignment=PAGE_SIZE;+if((start|size)&(min_alignment-1))+returnERR_PTR(-EINVAL);+if(start+size<start)+returnERR_PTR(-EOVERFLOW);++if(!private){+ret=__cma_check_range(start,size);+if(ret<0)+returnERR_PTR(ret);+}++cma=kmalloc(sizeof*cma,GFP_KERNEL);+if(!cma)+returnERR_PTR(-ENOMEM);++pool=gen_pool_create(ffs(min_alignment)-1,-1);+if(!pool){+ret=-ENOMEM;+gotoerror1;+}++ret=gen_pool_add(pool,start,size,-1);+if(unlikely(ret))+gotoerror2;++cma->migrate=!private;+cma->pool=pool;++pr_debug("%s: returning <%p>\n",__func__,(void*)cma);+returncma;++error2:+gen_pool_destroy(pool);+error1:+kfree(cma);+returnERR_PTR(ret);+}++voidcma_destroy(structcma*cma)+{+pr_debug("%s(<%p>)\n",__func__,(void*)cma);+gen_pool_destroy(cma->pool);+}+++/************************* Allocate and free *************************/++structcm{+structcma*cma;+unsignedlongphys,size;+atomic_tpinned,mapped;+};++/* Protects cm_alloc(), cm_free() as well as gen_pools of each cm. */+staticDEFINE_MUTEX(cma_mutex);++structcm*cm_alloc(structcma*cma,unsignedlongsize,+unsignedlongalignment)+{+unsignedlongstart;+intret=-ENOMEM;+structcm*cm;++pr_debug("%s(<%p>, %p/%p)\n",__func__,(void*)cma,+(void*)size,(void*)alignment);++if(!size||(alignment&(alignment-1)))+returnERR_PTR(-EINVAL);+size=PAGE_ALIGN(size);++cm=kmalloc(sizeof*cm,GFP_KERNEL);+if(!cm)+returnERR_PTR(-ENOMEM);++mutex_lock(&cma_mutex);++start=gen_pool_alloc_aligned(cma->pool,size,+alignment?ffs(alignment)-1:0);+if(!start)+gotoerror1;++if(cma->migrate){+unsignedlongpfn=phys_to_pfn(start);+ret=alloc_contig_range(pfn,pfn+(size>>PAGE_SHIFT),0);+if(ret){+pr_info("cma allocation failed\n");+gotoerror2;+}+}++mutex_unlock(&cma_mutex);++cm->cma=cma;+cm->phys=start;+cm->size=size;+atomic_set(&cm->pinned,0);+atomic_set(&cm->mapped,0);++pr_debug("%s(): returning [%p]\n",__func__,(void*)cm);+returncm;++error2:+gen_pool_free(cma->pool,start,size);+error1:+mutex_unlock(&cma_mutex);+kfree(cm);+returnERR_PTR(ret);+}+EXPORT_SYMBOL_GPL(cm_alloc);++voidcm_free(structcm*cm)+{+pr_debug("%s([%p])\n",__func__,(void*)cm);++if(WARN_ON(atomic_read(&cm->pinned)||atomic_read(&cm->mapped)))+return;++mutex_lock(&cma_mutex);++gen_pool_free(cm->cma->pool,cm->phys,cm->size);+if(cm->cma->migrate)+free_contig_pages(phys_to_page(cm->phys),+cm->size>>PAGE_SHIFT);++mutex_unlock(&cma_mutex);++kfree(cm);+}+EXPORT_SYMBOL_GPL(cm_free);+++/************************* Mapping and addresses *************************/++/*+*Currentlyno-operationsbutkeepreferencecountersforerror+*checking.+*/++unsignedlongcm_pin(structcm*cm)+{+pr_debug("%s([%p])\n",__func__,(void*)cm);+atomic_inc(&cm->pinned);+returncm->phys;+}+EXPORT_SYMBOL_GPL(cm_pin);++voidcm_unpin(structcm*cm)+{+pr_debug("%s([%p])\n",__func__,(void*)cm);+WARN_ON(!atomic_add_unless(&cm->pinned,-1,0));+}+EXPORT_SYMBOL_GPL(cm_unpin);++void*cm_vmap(structcm*cm)+{+pr_debug("%s([%p])\n",__func__,(void*)cm);+atomic_inc(&cm->mapped);+/*+*XXXWeshouldprobablydosomethingmorecleverinthe+*future.Thememorymightbehighmemafterall.+*/+returnphys_to_virt(cm->phys);+}+EXPORT_SYMBOL_GPL(cm_vmap);++voidcm_vunmap(structcm*cm)+{+pr_debug("%s([%p])\n",__func__,(void*)cm);+WARN_ON(!atomic_add_unless(&cm->mapped,-1,0));+}+EXPORT_SYMBOL_GPL(cm_vunmap);
@@ -5713,7 +5713,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,return-EINVAL;_start=start&(~0UL<<ret);-_end=alloc_contig_freed_pages(_start,end,flag);+_end=alloc_contig_freed_pages(_start,end,flags);/* Free head and tail (if any) */if(start!=_start)
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:16:23
From: Michal Nazarewicz <redacted>
This commit adds a gen_pool_alloc_aligned() function to the
generic allocator API. It allows specifying alignment for the
allocated block. This feature uses
the bitmap_find_next_zero_area_off() function.
It also fixes possible issue with bitmap's last element being
not fully allocated (ie. space allocated for chunk->bits is
not a multiple of sizeof(long)).
It also makes some other smaller changes:
- moves structure definitions out of the header file,
- adds __must_check to functions returning value,
- makes gen_pool_add() return -ENOMEM rater than -1 on error,
- changes list_for_each to list_for_each_entry, and
- makes use of bitmap_clear().
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/genalloc.h | 46 ++++++------
lib/genalloc.c | 182 ++++++++++++++++++++++++++-------------------
2 files changed, 129 insertions(+), 99 deletions(-)
@@ -8,29 +8,31 @@*Version2.SeethefileCOPYINGformoredetails.*/+structgen_pool;-/*-*Generalpurposespecialmemorypooldescriptor.-*/-structgen_pool{-rwlock_tlock;-structlist_headchunks;/* list of chunks in this pool */-intmin_alloc_order;/* minimum allocation order */-};+structgen_pool*__must_checkgen_pool_create(unsignedorder,intnid);-/*-*Generalpurposespecialmemorypoolchunkdescriptor.+int__must_checkgen_pool_add(structgen_pool*pool,unsignedlongaddr,+size_tsize,intnid);++voidgen_pool_destroy(structgen_pool*pool);++unsignedlong__must_check+gen_pool_alloc_aligned(structgen_pool*pool,size_tsize,+unsignedalignment_order);++/**+*gen_pool_alloc()-allocatespecialmemoryfromthepool+*@pool:Pooltoallocatefrom.+*@size:Numberofbytestoallocatefromthepool.+*+*Allocatetherequestednumberofbytesfromthespecifiedpool.+*Usesafirst-fitalgorithm.*/-structgen_pool_chunk{-spinlock_tlock;-structlist_headnext_chunk;/* next chunk in pool */-unsignedlongstart_addr;/* starting address of memory chunk */-unsignedlongend_addr;/* ending address of memory chunk */-unsignedlongbits[0];/* bitmap for allocating memory chunk */-};+staticinlineunsignedlong__must_check+gen_pool_alloc(structgen_pool*pool,size_tsize)+{+returngen_pool_alloc_aligned(pool,size,0);+}-externstructgen_pool*gen_pool_create(int,int);-externintgen_pool_add(structgen_pool*,unsignedlong,size_t,int);-externvoidgen_pool_destroy(structgen_pool*);-externunsignedlonggen_pool_alloc(structgen_pool*,size_t);-externvoidgen_pool_free(structgen_pool*,unsignedlong,size_t);+voidgen_pool_free(structgen_pool*pool,unsignedlongaddr,size_tsize);
@@ -16,53 +16,80 @@#include<linux/genalloc.h>+/* General purpose special memory pool descriptor. */+structgen_pool{+rwlock_tlock;/* protects chunks list */+structlist_headchunks;/* list of chunks in this pool */+unsignedorder;/* minimum allocation order */+};++/* General purpose special memory pool chunk descriptor. */+structgen_pool_chunk{+spinlock_tlock;/* protects bits */+structlist_headnext_chunk;/* next chunk in pool */+unsignedlongstart;/* start of memory chunk */+unsignedlongsize;/* number of bits */+unsignedlongbits[0];/* bitmap for allocating memory chunk */+};++/**-*gen_pool_create-createanewspecialmemorypool-*@min_alloc_order:logbase2ofnumberofbyteseachbitmapbitrepresents-*@nid:nodeidofthenodethepoolstructureshouldbeallocatedon,or-1+*gen_pool_create()-createanewspecialmemorypool+*@order:Logbase2ofnumberofbyteseachbitmapbit+*represents.+*@nid:Nodeidofthenodethepoolstructureshouldbeallocated+*on,or-1.Thiswillbealsousedforotherallocations.**Createanewspecialmemorypoolthatcanbeusedtomanagespecialpurpose*memorynotmanagedbytheregularkmalloc/kfreeinterface.*/-structgen_pool*gen_pool_create(intmin_alloc_order,intnid)+structgen_pool*__must_checkgen_pool_create(unsignedorder,intnid){structgen_pool*pool;-pool=kmalloc_node(sizeof(structgen_pool),GFP_KERNEL,nid);-if(pool!=NULL){+if(WARN_ON(order>=BITS_PER_LONG))+returnNULL;++pool=kmalloc_node(sizeof*pool,GFP_KERNEL,nid);+if(pool){rwlock_init(&pool->lock);INIT_LIST_HEAD(&pool->chunks);-pool->min_alloc_order=min_alloc_order;+pool->order=order;}returnpool;}EXPORT_SYMBOL(gen_pool_create);/**-*gen_pool_add-addanewchunkofspecialmemorytothepool-*@pool:pooltoaddnewmemorychunkto-*@addr:startingaddressofmemorychunktoaddtopool-*@size:sizeinbytesofthememorychunktoaddtopool-*@nid:nodeidofthenodethechunkstructureandbitmapshouldbe-*allocatedon,or-1+*gen_pool_add()-addanewchunkofspecialmemorytothepool+*@pool:Pooltoaddnewmemorychunkto.+*@addr:Startingaddressofmemorychunktoaddtopool.+*@size:Sizeinbytesofthememorychunktoaddtopool.**Addanewchunkofspecialmemorytothespecifiedpool.*/-intgen_pool_add(structgen_pool*pool,unsignedlongaddr,size_tsize,-intnid)+int__must_check+gen_pool_add(structgen_pool*pool,unsignedlongaddr,size_tsize,intnid){structgen_pool_chunk*chunk;-intnbits=size>>pool->min_alloc_order;-intnbytes=sizeof(structgen_pool_chunk)+-(nbits+BITS_PER_BYTE-1)/BITS_PER_BYTE;+size_tnbytes;++if(WARN_ON(!addr||addr+size<addr||+(addr&((1<<pool->order)-1))))+return-EINVAL;-chunk=kmalloc_node(nbytes,GFP_KERNEL|__GFP_ZERO,nid);-if(unlikely(chunk==NULL))-return-1;+size=size>>pool->order;+if(WARN_ON(!size))+return-EINVAL;++nbytes=sizeof*chunk+BITS_TO_LONGS(size)*sizeof*chunk->bits;+chunk=kzalloc_node(nbytes,GFP_KERNEL,nid);+if(!chunk)+return-ENOMEM;spin_lock_init(&chunk->lock);-chunk->start_addr=addr;-chunk->end_addr=addr+size;+chunk->start=addr>>pool->order;+chunk->size=size;write_lock(&pool->lock);list_add(&chunk->next_chunk,&pool->chunks);
@@ -73,115 +100,116 @@ int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size,EXPORT_SYMBOL(gen_pool_add);/**-*gen_pool_destroy-destroyaspecialmemorypool-*@pool:pooltodestroy+*gen_pool_destroy()-destroyaspecialmemorypool+*@pool:Pooltodestroy.**Destroythespecifiedspecialmemorypool.Verifiesthatthereareno*outstandingallocations.*/voidgen_pool_destroy(structgen_pool*pool){-structlist_head*_chunk,*_next_chunk;structgen_pool_chunk*chunk;-intorder=pool->min_alloc_order;-intbit,end_bit;-+intbit;-list_for_each_safe(_chunk,_next_chunk,&pool->chunks){-chunk=list_entry(_chunk,structgen_pool_chunk,next_chunk);+while(!list_empty(&pool->chunks)){+chunk=list_entry(pool->chunks.next,structgen_pool_chunk,+next_chunk);list_del(&chunk->next_chunk);-end_bit=(chunk->end_addr-chunk->start_addr)>>order;-bit=find_next_bit(chunk->bits,end_bit,0);-BUG_ON(bit<end_bit);+bit=find_next_bit(chunk->bits,chunk->size,0);+BUG_ON(bit<chunk->size);kfree(chunk);}kfree(pool);-return;}EXPORT_SYMBOL(gen_pool_destroy);/**-*gen_pool_alloc-allocatespecialmemoryfromthepool-*@pool:pooltoallocatefrom-*@size:numberofbytestoallocatefromthepool+*gen_pool_alloc_aligned()-allocatespecialmemoryfromthepool+*@pool:Pooltoallocatefrom.+*@size:Numberofbytestoallocatefromthepool.+*@alignment_order:Ordertheallocatedspaceshouldbe+*alignedto(eg.20meansallocatedspace+*mustbealignedto1MiB).**Allocatetherequestednumberofbytesfromthespecifiedpool.*Usesafirst-fitalgorithm.*/-unsignedlonggen_pool_alloc(structgen_pool*pool,size_tsize)+unsignedlong__must_check+gen_pool_alloc_aligned(structgen_pool*pool,size_tsize,+unsignedalignment_order){-structlist_head*_chunk;+unsignedlongaddr,align_mask=0,flags,start;structgen_pool_chunk*chunk;-unsignedlongaddr,flags;-intorder=pool->min_alloc_order;-intnbits,start_bit,end_bit;if(size==0)return0;-nbits=(size+(1UL<<order)-1)>>order;+if(alignment_order>pool->order)+align_mask=(1<<(alignment_order-pool->order))-1;-read_lock(&pool->lock);-list_for_each(_chunk,&pool->chunks){-chunk=list_entry(_chunk,structgen_pool_chunk,next_chunk);+size=(size+(1UL<<pool->order)-1)>>pool->order;-end_bit=(chunk->end_addr-chunk->start_addr)>>order;+read_lock(&pool->lock);+list_for_each_entry(chunk,&pool->chunks,next_chunk){+if(chunk->size<size)+continue;spin_lock_irqsave(&chunk->lock,flags);-start_bit=bitmap_find_next_zero_area(chunk->bits,end_bit,0,-nbits,0);-if(start_bit>=end_bit){+start=bitmap_find_next_zero_area_off(chunk->bits,chunk->size,+0,size,align_mask,+chunk->start);+if(start>=chunk->size){spin_unlock_irqrestore(&chunk->lock,flags);continue;}-addr=chunk->start_addr+((unsignedlong)start_bit<<order);--bitmap_set(chunk->bits,start_bit,nbits);+bitmap_set(chunk->bits,start,size);spin_unlock_irqrestore(&chunk->lock,flags);-read_unlock(&pool->lock);-returnaddr;+addr=(chunk->start+start)<<pool->order;+gotodone;}++addr=0;+done:read_unlock(&pool->lock);-return0;+returnaddr;}-EXPORT_SYMBOL(gen_pool_alloc);+EXPORT_SYMBOL(gen_pool_alloc_aligned);/**-*gen_pool_free-freeallocatedspecialmemorybacktothepool-*@pool:pooltofreeto-*@addr:startingaddressofmemorytofreebacktopool-*@size:sizeinbytesofmemorytofree+*gen_pool_free()-freeallocatedspecialmemorybacktothepool+*@pool:Pooltofreeto.+*@addr:Startingaddressofmemorytofreebacktopool.+*@size:Sizeinbytesofmemorytofree.**Freepreviouslyallocatedspecialmemorybacktothespecifiedpool.*/voidgen_pool_free(structgen_pool*pool,unsignedlongaddr,size_tsize){-structlist_head*_chunk;structgen_pool_chunk*chunk;unsignedlongflags;-intorder=pool->min_alloc_order;-intbit,nbits;-nbits=(size+(1UL<<order)-1)>>order;+if(!size)+return;-read_lock(&pool->lock);-list_for_each(_chunk,&pool->chunks){-chunk=list_entry(_chunk,structgen_pool_chunk,next_chunk);+addr=addr>>pool->order;+size=(size+(1UL<<pool->order)-1)>>pool->order;++BUG_ON(addr+size<addr);-if(addr>=chunk->start_addr&&addr<chunk->end_addr){-BUG_ON(addr+size>chunk->end_addr);+read_lock(&pool->lock);+list_for_each_entry(chunk,&pool->chunks,next_chunk)+if(addr>=chunk->start&&+addr+size<=chunk->start+chunk->size){spin_lock_irqsave(&chunk->lock,flags);-bit=(addr-chunk->start_addr)>>order;-while(nbits--)-__clear_bit(bit++,chunk->bits);+bitmap_clear(chunk->bits,addr-chunk->start,size);spin_unlock_irqrestore(&chunk->lock,flags);-break;+gotodone;}-}-BUG_ON(nbits>0);+BUG_ON(1);+done:read_unlock(&pool->lock);}EXPORT_SYMBOL(gen_pool_free);
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:16:25
From: KAMEZAWA Hiroyuki <redacted>
Memory hotplug is a logic for making pages unused in the specified
range of pfn. So, some of core logics can be used for other purpose
as allocating a very large contigous memory block.
This patch moves some functions from mm/memory_hotplug.c to
mm/page_isolation.c. This helps adding a function for large-alloc in
page_isolation.c with memory-unplug technique.
Signed-off-by: KAMEZAWA Hiroyuki <redacted>
[m.nazarewicz: reworded commit message]
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
[m.szyprowski: rebase and updated to v2.6.39-rc1 changes]
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/page-isolation.h | 7 +++
mm/memory_hotplug.c | 109 ---------------------------------------
mm/page_isolation.c | 111 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 118 insertions(+), 109 deletions(-)
@@ -33,5 +33,12 @@ test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn);externintset_migratetype_isolate(structpage*page);externvoidunset_migratetype_isolate(structpage*page);+/*+*Formigration.+*/++inttest_pages_in_a_zone(unsignedlongstart_pfn,unsignedlongend_pfn);+unsignedlongscan_lru_pages(unsignedlongstart,unsignedlongend);+intdo_migrate_range(unsignedlongstart_pfn,unsignedlongend_pfn);#endif
@@ -640,115 +640,6 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)}/*-*Confirmallpagesinarange[start,end)isbelongstothesamezone.-*/-staticinttest_pages_in_a_zone(unsignedlongstart_pfn,unsignedlongend_pfn)-{-unsignedlongpfn;-structzone*zone=NULL;-structpage*page;-inti;-for(pfn=start_pfn;-pfn<end_pfn;-pfn+=MAX_ORDER_NR_PAGES){-i=0;-/* This is just a CONFIG_HOLES_IN_ZONE check.*/-while((i<MAX_ORDER_NR_PAGES)&&!pfn_valid_within(pfn+i))-i++;-if(i==MAX_ORDER_NR_PAGES)-continue;-page=pfn_to_page(pfn+i);-if(zone&&page_zone(page)!=zone)-return0;-zone=page_zone(page);-}-return1;-}--/*-*Scanningpfnismucheasierthanscanninglrulist.-*ScanpfnfromstarttoendandFindLRUpage.-*/-staticunsignedlongscan_lru_pages(unsignedlongstart,unsignedlongend)-{-unsignedlongpfn;-structpage*page;-for(pfn=start;pfn<end;pfn++){-if(pfn_valid(pfn)){-page=pfn_to_page(pfn);-if(PageLRU(page))-returnpfn;-}-}-return0;-}--staticstructpage*-hotremove_migrate_alloc(structpage*page,unsignedlongprivate,int**x)-{-/* This should be improooooved!! */-returnalloc_page(GFP_HIGHUSER_MOVABLE);-}--#define NR_OFFLINE_AT_ONCE_PAGES (256)-staticint-do_migrate_range(unsignedlongstart_pfn,unsignedlongend_pfn)-{-unsignedlongpfn;-structpage*page;-intmove_pages=NR_OFFLINE_AT_ONCE_PAGES;-intnot_managed=0;-intret=0;-LIST_HEAD(source);--for(pfn=start_pfn;pfn<end_pfn&&move_pages>0;pfn++){-if(!pfn_valid(pfn))-continue;-page=pfn_to_page(pfn);-if(!page_count(page))-continue;-/*-*Wecanskipfreepages.Andwecanonlydealwithpageson-*LRU.-*/-ret=isolate_lru_page(page);-if(!ret){/* Success */-list_add_tail(&page->lru,&source);-move_pages--;-inc_zone_page_state(page,NR_ISOLATED_ANON+-page_is_file_cache(page));--}else{-#ifdef CONFIG_DEBUG_VM-printk(KERN_ALERT"removing pfn %lx from LRU failed\n",-pfn);-dump_page(page);-#endif-/* Becasue we don't have big zone->lock. we should-checkthisagainhere.*/-if(page_count(page)){-not_managed++;-ret=-EBUSY;-break;-}-}-}-if(!list_empty(&source)){-if(not_managed){-putback_lru_pages(&source);-gotoout;-}-/* this function returns # of failed pages */-ret=migrate_pages(&source,hotremove_migrate_alloc,0,-true,true);-if(ret)-putback_lru_pages(&source);-}-out:-returnret;-}--/**removefromfree_area[]andmarkallasReserved.*/staticint
@@ -139,3 +142,111 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn)spin_unlock_irqrestore(&zone->lock,flags);returnret?0:-EBUSY;}+++/*+*Confirmallpagesinarange[start,end)isbelongstothesamezone.+*/+inttest_pages_in_a_zone(unsignedlongstart_pfn,unsignedlongend_pfn)+{+unsignedlongpfn;+structzone*zone=NULL;+structpage*page;+inti;+for(pfn=start_pfn;+pfn<end_pfn;+pfn+=MAX_ORDER_NR_PAGES){+i=0;+/* This is just a CONFIG_HOLES_IN_ZONE check.*/+while((i<MAX_ORDER_NR_PAGES)&&!pfn_valid_within(pfn+i))+i++;+if(i==MAX_ORDER_NR_PAGES)+continue;+page=pfn_to_page(pfn+i);+if(zone&&page_zone(page)!=zone)+return0;+zone=page_zone(page);+}+return1;+}++/*+*Scanningpfnismucheasierthanscanninglrulist.+*ScanpfnfromstarttoendandFindLRUpage.+*/+unsignedlongscan_lru_pages(unsignedlongstart,unsignedlongend)+{+unsignedlongpfn;+structpage*page;+for(pfn=start;pfn<end;pfn++){+if(pfn_valid(pfn)){+page=pfn_to_page(pfn);+if(PageLRU(page))+returnpfn;+}+}+return0;+}++structpage*+hotremove_migrate_alloc(structpage*page,unsignedlongprivate,int**x)+{+/* This should be improooooved!! */+returnalloc_page(GFP_HIGHUSER_MOVABLE);+}++#define NR_OFFLINE_AT_ONCE_PAGES (256)+intdo_migrate_range(unsignedlongstart_pfn,unsignedlongend_pfn)+{+unsignedlongpfn;+structpage*page;+intmove_pages=NR_OFFLINE_AT_ONCE_PAGES;+intnot_managed=0;+intret=0;+LIST_HEAD(source);++for(pfn=start_pfn;pfn<end_pfn&&move_pages>0;pfn++){+if(!pfn_valid(pfn))+continue;+page=pfn_to_page(pfn);+if(!page_count(page))+continue;+/*+*Wecanskipfreepages.Andwecanonlydealwithpageson+*LRU.+*/+ret=isolate_lru_page(page);+if(!ret){/* Success */+list_add_tail(&page->lru,&source);+move_pages--;+inc_zone_page_state(page,NR_ISOLATED_ANON++page_is_file_cache(page));++}else{+#ifdef CONFIG_DEBUG_VM+printk(KERN_ALERT"removing pfn %lx from LRU failed\n",+pfn);+dump_page(page);+#endif+/* Because we don't have big zone->lock. we should+checkthisagainhere.*/+if(page_count(page)){+not_managed++;+ret=-EBUSY;+break;+}+}+}+if(!list_empty(&source)){+if(not_managed){+putback_lru_pages(&source);+gotoout;+}+/* this function returns # of failed pages */+ret=migrate_pages(&source,hotremove_migrate_alloc,MPOL_MF_MOVE_ALL,0,1);+if(ret)+putback_lru_pages(&source);+}+out:+returnret;+}
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:16:33
From: Michal Nazarewicz <redacted>
The MIGRATE_CMA migration type has two main characteristics:
(i) only movable pages can be allocated from MIGRATE_CMA
pageblocks and (ii) page allocator will never change migration
type of MIGRATE_CMA pageblocks.
This guarantees that page in a MIGRATE_CMA page block can
always be migrated somewhere else (unless there's no memory left
in the system).
It is designed to be used with Contiguous Memory Allocator
(CMA) for allocating big chunks (eg. 10MiB) of physically
contiguous memory. Once driver requests contiguous memory,
CMA will migrate pages from MIGRATE_CMA pageblocks.
To minimise number of migrations, MIGRATE_CMA migration type
is the last type tried when page allocator falls back to other
migration types then requested.
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/mmzone.h | 43 ++++++++++++++++++----
mm/Kconfig | 28 +++++++++------
mm/compaction.c | 10 +++++
mm/internal.h | 3 ++
mm/page_alloc.c | 93 ++++++++++++++++++++++++++++++++++++++----------
5 files changed, 140 insertions(+), 37 deletions(-)
@@ -35,13 +35,37 @@*/#define PAGE_ALLOC_COSTLY_ORDER 3-#define MIGRATE_UNMOVABLE 0-#define MIGRATE_RECLAIMABLE 1-#define MIGRATE_MOVABLE 2-#define MIGRATE_PCPTYPES 3 /* the number of types on the pcp lists */-#define MIGRATE_RESERVE 3-#define MIGRATE_ISOLATE 4 /* can't allocate from here */-#define MIGRATE_TYPES 5+enum{+MIGRATE_UNMOVABLE,+MIGRATE_RECLAIMABLE,+MIGRATE_MOVABLE,+MIGRATE_PCPTYPES,/* the number of types on the pcp lists */+MIGRATE_RESERVE=MIGRATE_PCPTYPES,+#ifdef CONFIG_MIGRATE_CMA+/*+*MIGRATE_CMAmigrationtypeisdesignedtomimictheway+*ZONE_MOVABLEworks.Onlymovablepagescanbeallocated+*fromMIGRATE_CMApageblocksandpageallocatornever+*implicitlychangemigrationtypeofMIGRATE_CMApageblock.+*+*Thewaytouseitistochangemigratetypeofarangeof+*pageblockstoMIGRATE_CMAwhichcanbedoneby+*__free_pageblock_cma()function.Whatisimportantthough+*isthatarangeofpageblocksmustbealignedto+*MAX_ORDER_NR_PAGESshouldbiggestpagebebiggerthen+*asinglepageblock.+*/+MIGRATE_CMA,+#endif+MIGRATE_ISOLATE,/* can't allocate from here */+MIGRATE_TYPES+};++#ifdef CONFIG_MIGRATE_CMA+# define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)+#else+# define is_migrate_cma(migratetype) false+#endif#define for_each_migratetype_order(order, type) \for(order=0;order<MAX_ORDER;order++)\
@@ -54,6 +78,11 @@ static inline int get_pageblock_migratetype(struct page *page)returnget_pageblock_flags_group(page,PB_migrate,PB_migrate_end);}+staticinlineboolis_pageblock_cma(structpage*page)+{+returnis_migrate_cma(get_pageblock_migratetype(page));+}+structfree_area{structlist_headfree_list[MIGRATE_TYPES];unsignedlongnr_free;
@@ -341,23 +341,29 @@ choiceendchoiceconfigCMA-bool"Contiguous Memory Allocator framework"-# Currently there is only one allocator so force it on+bool"Contiguous Memory Allocator"selectMIGRATIONselectGENERIC_ALLOCATORhelp-ThisenablestheContiguousMemoryAllocatorframeworkwhich-allowsdriverstoallocatebigphysically-contiguousblocksof-memoryforusewithhardwarecomponentsthatdonotsupportI/O-mapnorscatter-gather.+ThisenablestheContiguousMemoryAllocatorwhichallowsdrivers+toallocatebigphysically-contiguousblocksofmemoryforusewith+hardwarecomponentsthatdonotsupportI/Omapnorscatter-gather.-Ifyouselectthisoptionyouwillalsohavetoselect@least-oneallocatoralgorithmbelow.+Formoreinformationsee<include/linux/cma.h>.Ifunsure,say"n".++configMIGRATE_CMA+bool"Use MIGRATE_CMA migratetype"+depends onCMA+defaulty+help+ThisenablestheusetheMIGRATE_CMAmigratetypeintheCMA.+MIGRATE_CMAletsCMAworkonalmostarbitrarymemoryrangeand+notonlyinsideZONE_MOVABLE.-TomakeuseofCMAyouneedtospecifytheregionsand-driver->regionmappingoncommandlinewhenbootingthekernel.+ThisoptioncanalsobeselectedbycodethatusesMIGRATE_CMA+evenifCMAisnotpresent.-Formoreinformationsee<include/linux/cma.h>.Ifunsure,say"n".+Ifunsure,say"y".configCMA_DEBUGbool"CMA debug messages (DEVELOPEMENT)"
@@ -119,6 +119,16 @@ static bool suitable_migration_target(struct page *page)if(migratetype==MIGRATE_ISOLATE||migratetype==MIGRATE_RESERVE)returnfalse;+/* Keep MIGRATE_CMA alone as well. */+/*+*XXXRevisit.WecurrentlycannotletcompactiontouchCMA+*pagessincecompactioninsistsonchangingtheirmigration+*typetoMIGRATE_MOVABLE(seesplit_free_page()calledfrom+*isolate_freepages_block()above).+*/+if(is_migrate_cma(migratetype))+returnfalse;+/* If the page is a large free page, then allow migration */if(PageBuddy(page)&&page_order(page)>=pageblock_order)returntrue;
@@ -824,11 +848,15 @@ struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,*Thisarraydescribestheorderlistsarefallenbacktowhen*thefreelistsforthedesirablemigratetypearedepleted*/-staticintfallbacks[MIGRATE_TYPES][MIGRATE_TYPES-1]={+staticintfallbacks[MIGRATE_TYPES][4]={[MIGRATE_UNMOVABLE]={MIGRATE_RECLAIMABLE,MIGRATE_MOVABLE,MIGRATE_RESERVE},[MIGRATE_RECLAIMABLE]={MIGRATE_UNMOVABLE,MIGRATE_MOVABLE,MIGRATE_RESERVE},+#ifdef CONFIG_MIGRATE_CMA+[MIGRATE_MOVABLE]={MIGRATE_RECLAIMABLE,MIGRATE_UNMOVABLE,MIGRATE_CMA,MIGRATE_RESERVE},+#else[MIGRATE_MOVABLE]={MIGRATE_RECLAIMABLE,MIGRATE_UNMOVABLE,MIGRATE_RESERVE},-[MIGRATE_RESERVE]={MIGRATE_RESERVE,MIGRATE_RESERVE,MIGRATE_RESERVE},/* Never used */+#endif+[MIGRATE_RESERVE]={MIGRATE_RESERVE},/* Never used */};/*
@@ -923,12 +951,12 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)/* Find the largest possible block of pages in the other list */for(current_order=MAX_ORDER-1;current_order>=order;--current_order){-for(i=0;i<MIGRATE_TYPES-1;i++){+for(i=0;i<ARRAY_SIZE(fallbacks[0]);i++){migratetype=fallbacks[start_migratetype][i];/* MIGRATE_RESERVE handled later if necessary */if(migratetype==MIGRATE_RESERVE)-continue;+break;area=&(zone->free_area[current_order]);if(list_empty(&area->free_list[migratetype]))
@@ -943,19 +971,29 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)*pagestothepreferredallocationlist.Iffalling*backforareclaimablekernelallocation,bemore*agressiveabouttakingownershipoffreepages+*+*Ontheotherhand,neverchangemigration+*typeofMIGRATE_CMApageblocksnormoveCMA+*pagesondifferentfreelists.Wedon't+*wantunmovablepagestobeallocatedfrom+*MIGRATE_CMAareas.*/-if(unlikely(current_order>=(pageblock_order>>1))||-start_migratetype==MIGRATE_RECLAIMABLE||-page_group_by_mobility_disabled){-unsignedlongpages;+if(!is_pageblock_cma(page)&&+(unlikely(current_order>=pageblock_order/2)||+start_migratetype==MIGRATE_RECLAIMABLE||+page_group_by_mobility_disabled)){+intpages;pages=move_freepages_block(zone,page,-start_migratetype);+start_migratetype);-/* Claim the whole block if over half of it is free */+/*+*Claimthewholeblockifoverhalf+*ofitisfree+*/if(pages>=(1<<(pageblock_order-1))||-page_group_by_mobility_disabled)+page_group_by_mobility_disabled)set_pageblock_migratetype(page,-start_migratetype);+start_migratetype);migratetype=start_migratetype;}
@@ -965,11 +1003,14 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)rmv_page_order(page);/* Take ownership for orders >= pageblock_order */-if(current_order>=pageblock_order)+if(current_order>=pageblock_order&&+!is_pageblock_cma(page))change_pageblock_range(page,current_order,start_migratetype);-expand(zone,page,order,current_order,area,migratetype);+expand(zone,page,order,current_order,area,+is_migrate_cma(start_migratetype)+?start_migratetype:migratetype);trace_mm_page_alloc_extfrag(page,order,current_order,start_migratetype,migratetype);
@@ -1041,7 +1082,12 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,list_add(&page->lru,list);elselist_add_tail(&page->lru,list);-set_page_private(page,migratetype);+#ifdef CONFIG_MIGRATE_CMA+if(is_pageblock_cma(page))+set_page_private(page,MIGRATE_CMA);+else+#endif+set_page_private(page,migratetype);list=&page->lru;}__mod_zone_page_state(zone,NR_FREE_PAGES,-(i<<order));
@@ -1182,9 +1228,16 @@ void free_hot_cold_page(struct page *page, int cold)*offlinedbuttreatRESERVEasmovablepagessowecangetthose*areasbackifnecessary.Otherwise,wemayhavetofree*excessivelyintothepageallocator+*+*Still,donotchangemigrationtypeofMIGRATE_CMApages(if+*they'dberecordedasMIGRATE_MOVABLEanunmovablepagecould+*beallocatedfromMIGRATE_CMAblockandwedon'twanttoallow+*that).Inthisrespect,treatMIGRATE_CMAlike+*MIGRATE_ISOLATE.*/if(migratetype>=MIGRATE_PCPTYPES){-if(unlikely(migratetype==MIGRATE_ISOLATE)){+if(unlikely(migratetype==MIGRATE_ISOLATE+||is_migrate_cma(migratetype))){free_one_page(zone,page,0,migratetype);gotoout;}
@@ -1273,7 +1326,9 @@ int split_free_page(struct page *page)if(order>=pageblock_order-1){structpage*endpage=page+(1<<order)-1;for(;page<endpage;page+=pageblock_nr_pages)-set_pageblock_migratetype(page,MIGRATE_MOVABLE);+if(!is_pageblock_cma(page))+set_pageblock_migratetype(page,+MIGRATE_MOVABLE);}return1<<order;
@@ -5429,8 +5484,8 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count)*/if(zone_idx(zone)==ZONE_MOVABLE)returntrue;--if(get_pageblock_migratetype(page)==MIGRATE_MOVABLE)+if(get_pageblock_migratetype(page)==MIGRATE_MOVABLE||+is_pageblock_cma(page))returntrue;pfn=page_to_pfn(page);
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:16:34
From: Michal Nazarewicz <redacted>
This commit adds MIGRATE_CMA migratetype support to the CMA.
The advantage is that an (almost) arbitrary memory range can
be marked as MIGRATE_CMA which may not be the case with
ZONE_MOVABLE.
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/cma.h | 58 +++++++++++++++---
mm/cma.c | 167 ++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 197 insertions(+), 28 deletions(-)
@@ -118,12 +158,14 @@ unsigned long cma_reserve(unsigned long start, unsigned long size,**Therangemustbepagealigned.Differentcontextscannotoverlap.*-*Unless@privateistruethememoryrangemustlayinZONE_MOVABLE.-*If@privateistruenounderlayingmemorycheckingisdoneand-*duringallocationnopagesmigrationwillbeperformed-itis-*assumedthatthememoryisreservedandonlyCMAmanagesit.+*Unless@privateistruethememoryrangemusteitherlayin+*ZONE_MOVABLEormusthavebeeninitialisedwith+*cma_init_migratetype()function.If@privateistrueno+*underlayingmemorycheckingisdoneandduringallocationnopages+*migrationwillbeperformed-itisassumedthatthememoryis+*reservedandonlyCMAmanagesit.*-*@startand@sizemustbepageand@min_alignmentalignment.+*@startand@sizemustbepageand@min_alignmentaligned.*@min_alignmentspecifiestheminimalalignmentthatuserwillbe*abletorequestthroughcm_alloc()function.Inmostcasesone*willprobablypasszeroas@min_alignmentbutiftheCMAcontext
@@ -57,21 +57,132 @@ static unsigned long phys_to_pfn(phys_addr_t phys)/************************* Initialise CMA *************************/+#ifdef CONFIG_MIGRATE_CMA++staticstructcma_grabbed{+unsignedlongstart;+unsignedlongsize;+}cma_grabbed[8]__initdata;+staticunsignedcma_grabbed_count__initdata;++#ifdef CONFIG_DEBUG_VM++staticint__cma_give_back(unsignedlongstart,unsignedlongsize)+{+unsignedlongpfn=phys_to_pfn(start);+unsignedi=size>>PAGE_SHIFT;+structzone*zone;++pr_debug("%s(%p+%p)\n",__func__,(void*)start,(void*)size);++VM_BUG_ON(!pfn_valid(pfn));+zone=page_zone(pfn_to_page(pfn));++do{+VM_BUG_ON(!pfn_valid(pfn));+VM_BUG_ON(page_zone(pfn_to_page(pfn))!=zone);+if(!(pfn&(pageblock_nr_pages-1)))+__free_pageblock_cma(pfn_to_page(pfn));+++pfn;+++totalram_pages;+}while(--i);++return0;+}++#else++staticint__cma_give_back(unsignedlongstart,unsignedlongsize)+{+unsignedi=size>>(PAGE_SHIFT+pageblock_order);+structpage*p=phys_to_page(start);++pr_debug("%s(%p+%p)\n",__func__,(void*)start,(void*)size);++do{+__free_pageblock_cma(p);+p+=pageblock_nr_pages;+totalram_pages+=pageblock_nr_pages;+}while(--i);++return0;+}++#endif++staticint__init__cma_queue_give_back(unsignedlongstart,unsignedlongsize)+{+if(cma_grabbed_count==ARRAY_SIZE(cma_grabbed))+return-ENOSPC;++cma_grabbed[cma_grabbed_count].start=start;+cma_grabbed[cma_grabbed_count].size=size;+++cma_grabbed_count;+return0;+}++staticint(*cma_give_back)(unsignedlongstart,unsignedlongsize)=+__cma_queue_give_back;++staticint__initcma_give_back_queued(void)+{+structcma_grabbed*r=cma_grabbed;+unsignedi=cma_grabbed_count;++pr_debug("%s(): will give %u range(s)\n",__func__,i);++cma_give_back=__cma_give_back;++for(;i;--i,++r)+__cma_give_back(r->start,r->size);++return0;+}+subsys_initcall(cma_give_back_queued);++int__refcma_init_migratetype(unsignedlongstart,unsignedlongsize)+{+pr_debug("%s(%p+%p)\n",__func__,(void*)start,(void*)size);++if(!size)+return-EINVAL;+if((start|size)&((MAX_ORDER_NR_PAGES<<PAGE_SHIFT)-1))+return-EINVAL;+if(start+size<start)+return-EOVERFLOW;++returncma_give_back(start,size);+}++#endif+unsignedlongcma_reserve(unsignedlongstart,unsignedlongsize,-unsignedlongalignment)+unsignedlongalignment,boolinit_migratetype){pr_debug("%s(%p+%p/%p)\n",__func__,(void*)start,(void*)size,(void*)alignment);+#ifndef CONFIG_MIGRATE_CMA+if(init_migratetype)+return-EOPNOTSUPP;+#endif+/* Sanity checks */if(!size||(alignment&(alignment-1)))return(unsignedlong)-EINVAL;/* Sanitise input arguments */-start=PAGE_ALIGN(start);-size=PAGE_ALIGN(size);-if(alignment<PAGE_SIZE)-alignment=PAGE_SIZE;+if(init_migratetype){+start=ALIGN(start,MAX_ORDER_NR_PAGES<<PAGE_SHIFT);+size=ALIGN(size,MAX_ORDER_NR_PAGES<<PAGE_SHIFT);+if(alignment<(MAX_ORDER_NR_PAGES<<PAGE_SHIFT))+alignment=MAX_ORDER_NR_PAGES<<PAGE_SHIFT;+}else{+start=PAGE_ALIGN(start);+size=PAGE_ALIGN(size);+if(alignment<PAGE_SIZE)+alignment=PAGE_SIZE;+}/* Reserve memory */if(start){
@@ -94,6 +205,15 @@ unsigned long cma_reserve(unsigned long start, unsigned long size,}}+/* CMA Initialise */+if(init_migratetype){+intret=cma_init_migratetype(start,size);+if(ret<0){+memblock_free(start,size);+returnret;+}+}+returnstart;}
@@ -101,12 +221,13 @@ unsigned long cma_reserve(unsigned long start, unsigned long size,/************************** CMA context ***************************/structcma{-boolmigrate;+intmigratetype;structgen_pool*pool;};staticint__cma_check_range(unsignedlongstart,unsignedlongsize){+intmigratetype=MIGRATE_MOVABLE;unsignedlongpfn,count;structpage*page;structzone*zone;
@@ -115,8 +236,13 @@ static int __cma_check_range(unsigned long start, unsigned long size)if(WARN_ON(!pfn_valid(start)))return-EINVAL;+#ifdef CONFIG_MIGRATE_CMA+if(page_zonenum(pfn_to_page(start))!=ZONE_MOVABLE)+migratetype=MIGRATE_CMA;+#elseif(WARN_ON(page_zonenum(pfn_to_page(start))!=ZONE_MOVABLE))return-EINVAL;+#endif/* First check if all pages are valid and in the same zone */zone=page_zone(pfn_to_page(start));
@@ -134,20 +260,20 @@ static int __cma_check_range(unsigned long start, unsigned long size)page=pfn_to_page(start);count=(pfn-start)>>PAGE_SHIFT;do{-if(WARN_ON(get_pageblock_migratetype(page)!=MIGRATE_MOVABLE))+if(WARN_ON(get_pageblock_migratetype(page)!=migratetype))return-EINVAL;page+=pageblock_nr_pages;}while(--count);-return0;+returnmigratetype;}structcma*cma_create(unsignedlongstart,unsignedlongsize,unsignedlongmin_alignment,boolprivate){structgen_pool*pool;+intmigratetype,ret;structcma*cma;-intret;pr_debug("%s(%p+%p)\n",__func__,(void*)start,(void*)size);
@@ -162,10 +288,12 @@ struct cma *cma_create(unsigned long start, unsigned long size,if(start+size<start)returnERR_PTR(-EOVERFLOW);-if(!private){-ret=__cma_check_range(start,size);-if(ret<0)-returnERR_PTR(ret);+if(private){+migratetype=0;+}else{+migratetype=__cma_check_range(start,size);+if(migratetype<0)+returnERR_PTR(migratetype);}cma=kmalloc(sizeof*cma,GFP_KERNEL);
@@ -182,7 +310,7 @@ struct cma *cma_create(unsigned long start, unsigned long size,if(unlikely(ret))gotoerror2;-cma->migrate=!private;+cma->migratetype=migratetype;cma->pool=pool;pr_debug("%s: returning <%p>\n",__func__,(void*)cma);
@@ -238,13 +366,12 @@ struct cm *cm_alloc(struct cma *cma, unsigned long size,if(!start)gotoerror1;-if(cma->migrate){+if(cma->migratetype){unsignedlongpfn=phys_to_pfn(start);-ret=alloc_contig_range(pfn,pfn+(size>>PAGE_SHIFT),0);-if(ret){-pr_info("cma allocation failed\n");+ret=alloc_contig_range(pfn,pfn+(size>>PAGE_SHIFT),+0,cma->migratetype);+if(ret)gotoerror2;-}}mutex_unlock(&cma_mutex);
@@ -277,7 +404,7 @@ void cm_free(struct cm *cm)mutex_lock(&cma_mutex);gen_pool_free(cm->cma->pool,cm->phys,cm->size);-if(cm->cma->migrate)+if(cm->cma->migratetype)free_contig_pages(phys_to_page(cm->phys),cm->size>>PAGE_SHIFT);
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:17:53
From: Michal Nazarewicz <redacted>
This commit adds the alloc_contig_range() function which tries
to allecate given range of pages. It tries to migrate all
already allocated pages that fall in the range thus freeing them.
Once all pages in the range are freed they are removed from the
buddy system thus allocated for the caller to use.
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/page-isolation.h | 2 +
mm/page_alloc.c | 144 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 146 insertions(+), 0 deletions(-)
@@ -5583,6 +5583,150 @@ unsigned long alloc_contig_freed_pages(unsigned long start, unsigned long end,returnpfn;}+staticunsignedlongpfn_to_maxpage(unsignedlongpfn)+{+returnpfn&~(MAX_ORDER_NR_PAGES-1);+}++staticunsignedlongpfn_to_maxpage_up(unsignedlongpfn)+{+returnALIGN(pfn,MAX_ORDER_NR_PAGES);+}++#define MIGRATION_RETRY 5+staticint__alloc_contig_migrate_range(unsignedlongstart,unsignedlongend)+{+intmigration_failed=0,ret;+unsignedlongpfn=start;++/*+*Somecode"borrowed"fromKAMEZAWAHiroyuki's+*__alloc_contig_pages().+*/++for(;;){+pfn=scan_lru_pages(pfn,end);+if(!pfn||pfn>=end)+break;++ret=do_migrate_range(pfn,end);+if(!ret){+migration_failed=0;+}elseif(ret!=-EBUSY+||++migration_failed>=MIGRATION_RETRY){+returnret;+}else{+/* There are unstable pages.on pagevec. */+lru_add_drain_all();+/*+*theremaybepagesonpcplistbefore+*wemarktherangeasISOLATED.+*/+drain_all_pages();+}+cond_resched();+}++if(!migration_failed){+/* drop all pages in pagevec and pcp list */+lru_add_drain_all();+drain_all_pages();+}++/* Make sure all pages are isolated */+if(WARN_ON(test_pages_isolated(start,end)))+return-EBUSY;++return0;+}++/**+*alloc_contig_range()--triestoallocategivenrangeofpages+*@start:startPFNtoallocate+*@end:one-past-the-lastPFNtoallocate+*@flags:flagspassedtoalloc_contig_freed_pages().+*+*ThePFNrangedoesnothavetobepageblockorMAX_ORDER_NR_PAGES+*aligned,hovewerit'scallersresponsibilitytoguaranteethatwe+*aretheonlythreadthatchangesmigratetypeofpageblocksthe+*pagesfallin.+*+*Returnszeroonsuccessornegativeerrorcode.Onsuccessall+*pageswhichPFNisin(start,end)areallocatedforthecallerand+*needtobefreedwithfree_contig_pages().+*/+intalloc_contig_range(unsignedlongstart,unsignedlongend,+gfp_tflags)+{+unsignedlong_start,_end;+intret;++/*+*Whatwedohereiswemarkallpageblocksinrangeas+*MIGRATE_ISOLATE.Becauseofthewaypageallocatorwork,we+*aligntherangetoMAX_ORDERpagessothatpageallocator+*won'ttrytomergebuddiesfromdifferentpageblocksand+*changeMIGRATE_ISOLATEtosomeothermigrationtype.+*+*OncethepageblocksaremarkedasMIGRATE_ISOLATE,we+*migratethepagesfromanunalignedrange(ie.pagesthat+*weareinterestedin).Thiswillputallthepagesin+*rangebacktopageallocatorasMIGRATE_ISOLATE.+*+*Whenthisisdone,wetakethepagesinrangefrompage+*allocatorremovingthemfromthebuddysystem.Thisway+*pageallocatorwillneverconsiderusingthem.+*+*Thisletsusmarkthepageblocksbackas+*MIGRATE_CMA/MIGRATE_MOVABLEsothatfreepagesinthe+*MAX_ORDERalignedrangebutnotintheunaligned,original+*rangeareputbacktopageallocatorsothatbuddycanuse+*them.+*/++ret=start_isolate_page_range(pfn_to_maxpage(start),+pfn_to_maxpage_up(end));+if(ret)+gotodone;++ret=__alloc_contig_migrate_range(start,end);+if(ret)+gotodone;++/*+*Pagesfrom[start,end)arewithinaMAX_ORDER_NR_PAGES+*alignedblocksthataremarkedasMIGRATE_ISOLATE.What's+*more,allpagesin[start,end)arefreeinpageallocator.+*Whatwearegoingtodoistoallocateallpagesfrom+*[start,end)(thatisremovethemfrompageallocater).+*+*Theonlyproblemisthatpagesatthebeginningandatthe+*endofinterestingrangemaybenotalignedwithpagesthat+*pageallocatorholds,ie.theycanbepartofhigherorder+*pages.Becauseofthis,wereservethebiggerrangeand+*oncethisisdonefreethepageswearenotinterestedin.+*/++ret=0;+while(!PageBuddy(pfn_to_page(start&(~0UL<<ret))))+if(WARN_ON(++ret>=MAX_ORDER))+return-EINVAL;++_start=start&(~0UL<<ret);+_end=alloc_contig_freed_pages(_start,end,flag);++/* Free head and tail (if any) */+if(start!=_start)+free_contig_pages(pfn_to_page(_start),start-_start);+if(end!=_end)+free_contig_pages(pfn_to_page(end),_end-end);++ret=0;+done:+undo_isolate_page_range(pfn_to_maxpage(start),pfn_to_maxpage_up(end));+returnret;+}+voidfree_contig_pages(structpage*page,intnr_pages){for(;nr_pages;--nr_pages,++page)
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:18:24
From: Michal Nazarewicz <redacted>
This commit changes various functions that change pages and
pageblocks migrate type between MIGRATE_ISOLATE and
MIGRATE_MOVABLE in such a way as to allow to work with
MIGRATE_CMA migrate type.
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/page-isolation.h | 40 +++++++++++++++++++++++++++-------------
mm/page_alloc.c | 19 ++++++++++++-------
mm/page_isolation.c | 15 ++++++++-------
3 files changed, 47 insertions(+), 27 deletions(-)
@@ -5700,6 +5700,10 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)*@start:startPFNtoallocate*@end:one-past-the-lastPFNtoallocate*@flags:flagspassedtoalloc_contig_freed_pages().+*@migratetype:migratetypeoftheunderlayingpageblocks(either+*#MIGRATE_MOVABLEor#MIGRATE_CMA).Allpageblocks+*inrangemusthavethesamemigratetypeanditmust+*beeitherofthetwo.**ThePFNrangedoesnothavetobepageblockorMAX_ORDER_NR_PAGES*aligned,hovewerit'scallersresponsibilitytoguaranteethatwe
@@ -5711,7 +5715,7 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)*needtobefreedwithfree_contig_pages().*/intalloc_contig_range(unsignedlongstart,unsignedlongend,-gfp_tflags)+gfp_tflags,unsignedmigratetype){unsignedlong_start,_end;intret;
@@ -5739,8 +5743,8 @@ int alloc_contig_range(unsigned long start, unsigned long end,*them.*/-ret=start_isolate_page_range(pfn_to_maxpage(start),-pfn_to_maxpage_up(end));+ret=__start_isolate_page_range(pfn_to_maxpage(start),+pfn_to_maxpage_up(end),migratetype);if(ret)gotodone;
@@ -5778,7 +5782,8 @@ int alloc_contig_range(unsigned long start, unsigned long end,ret=0;done:-undo_isolate_page_range(pfn_to_maxpage(start),pfn_to_maxpage_up(end));+__undo_isolate_page_range(pfn_to_maxpage(start),pfn_to_maxpage_up(end),+migratetype);returnret;}
@@ -23,10 +23,11 @@ __first_valid_page(unsigned long pfn, unsigned long nr_pages)}/*-*start_isolate_page_range()--makepage-allocation-typeofrangeofpages+*__start_isolate_page_range()--makepage-allocation-typeofrangeofpages*tobeMIGRATE_ISOLATE.*@start_pfn:ThelowerPFNoftherangetobeisolated.*@end_pfn:TheupperPFNoftherangetobeisolated.+*@migratetype:migratetypetosetinerrorrecovery.**Makingpage-allocation-typetobeMIGRATE_ISOLATEmeansfreepagesin*therangewillneverbeallocated.Anyfreepagesandpagesfreedinthe
@@ -35,8 +36,8 @@ __first_valid_page(unsigned long pfn, unsigned long nr_pages)*start_pfn/end_pfnmustbealignedtopageblock_order.*Returns0onsuccessand-EBUSYifanypartofrangecannotbeisolated.*/-int-start_isolate_page_range(unsignedlongstart_pfn,unsignedlongend_pfn)+int__start_isolate_page_range(unsignedlongstart_pfn,unsignedlongend_pfn,+unsignedmigratetype){unsignedlongpfn;unsignedlongundo_pfn;
@@ -80,7 +81,7 @@ undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn)page=__first_valid_page(pfn,pageblock_nr_pages);if(!page||get_pageblock_migratetype(page)!=MIGRATE_ISOLATE)continue;-unset_migratetype_isolate(page);+__unset_migratetype_isolate(page,migratetype);}return0;}
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:18:55
From: Michal Nazarewicz <redacted>
This commit adds a bitmap_find_next_zero_area_off() function which
works like bitmap_find_next_zero_area() function expect it allows an
offset to be specified when alignment is checked. This lets caller
request a bit such that its number plus the offset is aligned
according to the mask.
Signed-off-by: Michal Nazarewicz <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/bitmap.h | 24 +++++++++++++++++++-----
lib/bitmap.c | 22 ++++++++++++----------
2 files changed, 31 insertions(+), 15 deletions(-)
@@ -113,11 +114,24 @@ extern int __bitmap_weight(const unsigned long *bitmap, int bits);externvoidbitmap_set(unsignedlong*map,inti,intlen);externvoidbitmap_clear(unsignedlong*map,intstart,intnr);-externunsignedlongbitmap_find_next_zero_area(unsignedlong*map,-unsignedlongsize,-unsignedlongstart,-unsignedintnr,-unsignedlongalign_mask);++externunsignedlongbitmap_find_next_zero_area_off(unsignedlong*map,+unsignedlongsize,+unsignedlongstart,+unsignedintnr,+unsignedlongalign_mask,+unsignedlongalign_offset);++staticinlineunsignedlong+bitmap_find_next_zero_area(unsignedlong*map,+unsignedlongsize,+unsignedlongstart,+unsignedintnr,+unsignedlongalign_mask)+{+returnbitmap_find_next_zero_area_off(map,size,start,nr,+align_mask,0);+}externintbitmap_scnprintf(char*buf,unsignedintlen,constunsignedlong*src,intnbits);
@@ -315,30 +315,32 @@ void bitmap_clear(unsigned long *map, int start, int nr)}EXPORT_SYMBOL(bitmap_clear);-/*+/***bitmap_find_next_zero_area-findacontiguousalignedzeroarea*@map:Theaddresstobasethesearchon*@size:Thebitmapsizeinbits*@start:Thebitnumbertostartsearchingat*@nr:Thenumberofzeroedbitswe'relookingfor*@align_mask:Alignmentmaskforzeroarea+*@align_offset:Alignmentoffsetforzeroarea.**The@align_maskshouldbeonelessthanapowerof2;theeffectisthat-*thebitoffsetofallzeroareasthisfunctionfindsismultiplesofthat-*powerof2.A@align_maskof0meansnoalignmentisrequired.+*thebitoffsetofallzeroareasthisfunctionfindsplus@align_offset+*ismultipleofthatpowerof2.*/-unsignedlongbitmap_find_next_zero_area(unsignedlong*map,-unsignedlongsize,-unsignedlongstart,-unsignedintnr,-unsignedlongalign_mask)+unsignedlongbitmap_find_next_zero_area_off(unsignedlong*map,+unsignedlongsize,+unsignedlongstart,+unsignedintnr,+unsignedlongalign_mask,+unsignedlongalign_offset){unsignedlongindex,end,i;again:index=find_next_zero_bit(map,size,start);/* Align allocation */-index=__ALIGN_MASK(index,align_mask);+index=__ALIGN_MASK(index+align_offset,align_mask)-align_offset;end=index+nr;if(end>size)
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-03-31 13:19:15
This patch adds CMA 'regions style' API (almost compatible with CMA v1).
It is intended mainly for testing the real contigous memory allocator
and page migration with devices that use older CMA api.
Based on previous works by Michal Nazarewicz.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Michal Nazarewicz <redacted>
---
include/linux/cma-regions.h | 340 +++++++++++++++++++
include/linux/cma.h | 3 +
mm/Makefile | 2 +-
mm/cma-regions.c | 759 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1103 insertions(+), 1 deletions(-)
create mode 100644 include/linux/cma-regions.h
create mode 100644 mm/cma-regions.c
@@ -0,0 +1,340 @@+#ifndef __LINUX_CMA_REGIONS_H+#define __LINUX_CMA_REGIONS_H++/*+*ContiguousMemoryAllocatorframework-memoryregionmanagement+*Copyright(c)2010-2011bySamsungElectronics.+*WrittenbyMichalNazarewicz(m.nazarewiczatsamsung.com)+*UpdatedbyMarekSzyprowski(m.szyprowskiatsamsung.com)+*/++/*+*SeeDocumentation/contiguous-memory.txtfordetails.+*/++#include<linux/ioctl.h>+#include<linux/types.h>+#include<linux/err.h>+#include<linux/init.h>++/***************************** Kernel level API *****************************/++#include<linux/rbtree.h>+#include<linux/list.h>+++structdevice;+structcma_info;+structcm;++/*+*Don'tcallitdirectly,usecma_alloc(),cma_alloc_from()or+*cma_alloc_from_region().+*/+structcm*__must_check+__cma_alloc(conststructdevice*dev,constchar*type,+size_tsize,dma_addr_talignment);++/* Don't call it directly, use cma_info() or cma_info_about(). */+int+__cma_info(structcma_info*info,conststructdevice*dev,constchar*type);+++/**+*cma_alloc-allocatescontiguouschunkofmemory.+*@dev:Thedevicetoperformallocationfor.+*@type:Atypeofmemorytoallocate.Platformmaydefine+*severaldifferenttypesofmemoryanddevicedrivers+*canthenrequestchunksofdifferenttypes.Usuallyit's+*safetopassNULLherewhichisthesameaspassing+*"common".+*@size:Sizeofthememorytoallocateinbytes.+*@alignment:Desiredalignmentinbytes.Mustbeapoweroftwoor+*zero.Ifalignmentislessthenapagesizeitwillbe+*settopagesize.Ifunsure,passzerohere.+*+*Onerrorreturnsanegativeerrorcasttodma_addr_t.Use+*IS_ERR_VALUE()tocheckifreturnedvalueisindeedanerror.+*Otherwisebusaddressofthechunkisreturned.+*/+staticinlinestructcm*__must_check+cma_alloc(conststructdevice*dev,constchar*type,+size_tsize,dma_addr_talignment)+{+returndev?__cma_alloc(dev,type,size,alignment):ERR_PTR(-EINVAL);+}+++/**+*structcma_info-informationaboutregionsreturnedbycma_info().+*@lower_bound:Thesmallestaddressthatispossibletobe+*allocatedforgiven(dev,type)pair.+*@upper_bound:Theonebyteafterthebiggestaddressthatis+*possibletobeallocatedforgiven(dev,type)+*pair.+*@total_size:Totalsizeofregionsmappedto(dev,type)pair.+*@free_size:Totalfreesizeinalloftheregionsmappedto(dev,type)+*pair.Becauseofpossibleraceconditions,itisnot+*guaranteedthatthevaluewillbecorrect--itgivesonly+*anapproximation.+*@count:Numberofregionsmappedto(dev,type)pair.+*/+structcma_info{+dma_addr_tlower_bound,upper_bound;+size_ttotal_size,free_size;+unsignedcount;+};++/**+*cma_info-queriesinformationaboutregions.+*@info:Pointertoastructurewheretosavetheinformation.+*@dev:Thedevicetoqueryinformationfor.+*@type:Atypeofmemorytoqueryinformationfor.+*Ifunsure,passNULLherewhichisequaltopassing+*"common".+*+*Onerrorreturnsanegativeerror,zerootherwise.+*/+staticinlineint+cma_info(structcma_info*info,conststructdevice*dev,constchar*type)+{+returndev?__cma_info(info,dev,type):-EINVAL;+}+++/**+*cma_free-freesachunkofmemory.+*@addr:Beginningofthechunk.+*+*Returns-ENOENTifthereisnochunkatgivenlocation;otherwise+*zero.Intheformercaseissuesawarning.+*/+intcma_free(structcm*addr);++++/****************************** Lower lever API *****************************/++/**+*cma_alloc_from-allocatescontiguouschunkofmemoryfromnamedregions.+*@regions:Commaseparatedlistofregionnames.TerminatedbyNUL+*byteorasemicolon.+*@size:Sizeofthememorytoallocateinbytes.+*@alignment:Desiredalignmentinbytes.Mustbeapoweroftwoor+*zero.Ifalignmentislessthenapagesizeitwillbe+*settopagesize.Ifunsure,passzerohere.+*+*Onerrorreturnsanegativeerrorcasttodma_addr_t.Use+*IS_ERR_VALUE()tocheckifreturnedvalueisindeedanerror.+*Otherwisebusaddressofthechunkisreturned.+*/+staticinlinestructcm*__must_check+cma_alloc_from(constchar*regions,size_tsize,dma_addr_talignment)+{+return__cma_alloc(NULL,regions,size,alignment);+}++/**+*cma_info_about-queriesinformationaboutnamedregions.+*@info:Pointertoastructurewheretosavetheinformation.+*@regions:Commaseparatedlistofregionnames.TerminatedbyNUL+*byteorasemicolon.+*+*Onerrorreturnsanegativeerror,zerootherwise.+*/+staticinlineint+cma_info_about(structcma_info*info,constconstchar*regions)+{+return__cma_info(info,NULL,regions);+}+++/**+*structcma_region-aregionreservedforCMAallocations.+*@name:Uniquenameoftheregion.Readonly.+*@start:Busaddressoftheregioninbytes.Alwaysalignedat+*leasttoafullpage.Readonly.+*@size:Sizeoftheregioninbytes.Multiplyofapagesize.+*Readonly.+*@alignment:Desiredalignmentoftheregioninbytes.Apoweroftwo,+*alwaysatleastpagesize.Early.+*@cma:Lowlevelmemoryareregisteredtokernelmmsubsystem.+*Private.+*@list:Entryinlistofregions.Private.+*@used:Whetherregionwasalreadyused,ie.therewasatleast+*oneallocationrequestfor.Private.+*@registered:Whetherthisregionhasbeenregistered.Readonly.+*@reserved:Whetherthisregionhasbeenreserved.Early.Readonly.+*@copy_name:Whether@nameneedstobecopiedwhenthisregionis+*convertedfromearlytonormal.Early.Private.+*+*Regionscomeintwotypes:anearlyregionandnormalregion.The+*formercanbereservedornot-reserved.Fieldsmarkedas"early"+*areonlymeaningfulinearlyregions.+*+*Earlyregionsareimportantonlyduringinitialisation.Thelist+*ofearlyregionsisbuiltfromthe"cma"commandlineargumentor+*platformdefaults.Platforminitialisationcodeisresponsiblefor+*reservingspaceforunreservedregionsthatareplacedon+*cma_early_regionslist.+*+*Later,duringCMAinitialisationallreservedregionsfromthe+*cma_early_regionslistareregisteredasnormalregionsandcanbe+*usedusingstandardmechanisms.+*/+structcma_region{+constchar*name;+dma_addr_tstart;+size_tsize;+dma_addr_talignment;/* Early region */++structcma*cma;+structlist_headlist;++unsignedused:1;+unsignedregistered:1;+unsignedreserved:1;+unsignedcopy_name:1;+};+++/**+*cma_region_register()-registersaregion.+*@reg:Regiontoregion.+*+*Region'sstartandsizemustbeset.+*+*Ifnameissettheregionwillbeaccessibleusingnormalmechanism+*likemappingorcma_alloc_from()functionotherwiseitwillbe+*aprivateregionandaccessibleonlyusingthe+*cma_alloc_from_region()function.+*+*Ifallocissetfunctionwilltrytoinitialisegivenallocator+*(andwillreturnerrorifitfailes).Otherwisealloc_namemay+*pointtoanameofanallocatortouse(ifnotset,thedefault+*willbeused).+*+*Allotherfieldsareignoredand/oroverwritten.+*+*Returnszeroornegativeerror.Inparticular,-EADDRINUSEif+*regionoverlapwithalreadyexistingregion.+*/+int__must_checkcma_region_register(structcma_region*reg);++/**+*cma_region_unregister()-unregistersaregion.+*@reg:Regiontounregister.+*+*Regionisunregisteredonlyiftherearenochunksallocatedfor+*it.Otherwise,functionreturns-EBUSY.+*+*Onsuccessreturszero.+*/+int__must_checkcma_region_unregister(structcma_region*reg);+++/**+*cma_alloc_from_region()-allocatescontiguouschunkofmemoryfromregion.+*@reg:Regiontoallocatechunkfrom.+*@size:Sizeofthememorytoallocateinbytes.+*@alignment:Desiredalignmentinbytes.Mustbeapoweroftwoor+*zero.Ifalignmentislessthenapagesizeitwillbe+*settopagesize.Ifunsure,passzerohere.+*+*Onerrorreturnsanegativeerrorcasttodma_addr_t.Use+*IS_ERR_VALUE()tocheckifreturnedvalueisindeedanerror.+*Otherwisebusaddressofthechunkisreturned.+*/+structcm*__must_check+cma_alloc_from_region(structcma_region*reg,+size_tsize,dma_addr_talignment);+++/**************************** Initialisation API ****************************/++/**+*cma_set_defaults()-specifiesdefaultcommandlineparameters.+*@regions:Azero-sizedentryterminatedlistofearlyregions.+*Thisarraymustnotbeplacedin__initdatasection.+*@map:Mapattribute.+*+*Thisfunctionshouldbecalledpriortocma_early_regions_reserve()+*andafterearlyparametershavebeenparsed.+*+*Returnszeroornegativeerror.+*/+int__initcma_set_defaults(structcma_region*regions,constchar*map);+++/**+*cma_early_regions-alistofearlyregions.+*+*Platformneedstoallocatespaceforeachoftheregionbefore+*initcallsareexecuted.Ifspaceisreserved,thereservedflag+*mustbeset.Platforminitialisationcodemaychoosetouse+*cma_early_regions_allocate().+*+*Later,duringCMAinitialisationallreservedregionsfromthe+*cma_early_regionslistareregisteredasnormalregionsandcanbe+*usedusingstandardmechanisms.+*/+externstructlist_headcma_early_regions__initdata;+++/**+*cma_early_region_register()-registersanearlyregion.+*@reg:Regiontoadd.+*+*Region'ssize,startandalignmentmustbeset(howeverthelast+*twocanbezero).Ifnameissettheregionwillbeaccessible+*usingnormalmechanismlikemappingorcma_alloc_from()function+*otherwiseitwillbeaprivateregionaccessibleonlyusingthe+*cma_alloc_from_region().+*+*Duringplatforminitialisation,spaceisreservedforearly+*regions.Later,whenCMAinitialises,theearlyregionsare+*"converted"intonormalregions.Ifcma_region::allocisset,CMA+*willthentrytosetupgivenallocatorontheregion.Failureto+*dosowillresultintheregionnotbeingregisteredeventhough+*thespaceforitwillstillbereserved.Ifcma_region::allocis+*notset,allocatorwillbeattachedtotheregiononfirstuseand+*thevalueofcma_region::alloc_namewillbetakenintoaccountif+*set.+*+*Allotherfieldsareignoredand/oroverwritten.+*+*Returnszeroornegativeerror.Nocheckingifregionsoverlapis+*performed.+*/+int__init__must_checkcma_early_region_register(structcma_region*reg);+++/**+*cma_early_region_reserve()-reservesaphysicallycontiguousmemoryregion.+*@reg:Earlyregiontoreservememoryfor.+*+*Ifplatformsupportsbootmemthisisthefirstallocatorthis+*functiontriestouse.Ifthatfailes(orbootmemisnot+*supported)functiontriestousememblecifitisavailable.+*+*Onsuccesssetsreg->reservedflag.+*+*Returnszeroornegativeerror.+*/+int__initcma_early_region_reserve(structcma_region*reg);++/**+*cma_early_regions_reserve()-helperfunctionforreservingearlyregions.+*@reserve:Callbacfunctionusedtoreservespaceforregion.Needs+*toreturnnon-negativeifallocationsucceeded,negative+*errorotherwise.NULLmeanscma_early_region_alloc()will+*beused.+*+*Thisfunctiontraversesthe%cma_early_regionslistandtriesto+*reservememoryforeachearlyregion.Itusesthe@reserve+*callbackfunctionforthatpurpose.Thereservedflagofeach+*regionisupdatedaccordingly.+*/+void__initcma_early_regions_reserve(int(*reserve)(structcma_region*reg));++#endif
@@ -0,0 +1,759 @@+/*+*ContiguousMemoryAllocatorframework-memoryregionmanagement+*Copyright(c)2010-2011bySamsungElectronics.+*WrittenbyMichalNazarewicz(m.nazarewiczatsamsung.com)+*UpdatedbyMarekSzyprowski(m.szyprowskiatsamsung.com)+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicenseas+*publishedbytheFreeSoftwareFoundation;eitherversion2ofthe+*Licenseor(atyouroptional)anylaterversionofthelicense.+*/++/*+*SeeDocumentation/contiguous-memory.txtfordetails.+*/++#define pr_fmt(fmt) "cma: " fmt++#ifdef CONFIG_CMA_DEBUG+# define DEBUG+#endif++#include<linux/device.h> /* struct device, dev_name() */+#include<linux/errno.h> /* Error numbers */+#include<linux/err.h> /* IS_ERR, PTR_ERR, etc. */+#include<linux/mm.h> /* PAGE_ALIGN() */+#include<linux/module.h> /* EXPORT_SYMBOL_GPL() */+#include<linux/mutex.h> /* mutex */+#include<linux/slab.h> /* kmalloc() */+#include<linux/string.h> /* str*() */++#include<linux/cma.h>++/*+*Protectscma_regions,cma_map,cma_map_length.+*/+staticDEFINE_MUTEX(cma_mutex);+++/************************* Map attribute *************************/++staticconstchar*cma_map;+staticsize_tcma_map_length;++/*+*map-attr::=[rules[';']]+*rules::=rule[';'rules]+*rule::=patterns'='regions+*patterns::=pattern[','patterns]+*regions::=REG-NAME[','regions]+*pattern::=dev-pattern['/'TYPE-NAME]|'/'TYPE-NAME+*+*SeeDocumentation/contiguous-memory.txtfordetails.+*/+staticssize_tcma_map_validate(constchar*param)+{+constchar*ch=param;++if(*ch=='\0'||*ch=='\n')+return0;++for(;;){+constchar*start=ch;++while(*ch&&*ch!='\n'&&*ch!=';'&&*ch!='=')+++ch;++if(*ch!='='||start==ch){+pr_err("map: expecting \"<patterns>=<regions>\" near %s\n",+start);+return-EINVAL;+}++while(*++ch!=';')+if(*ch=='\0'||*ch=='\n')+returnch-param;+if(ch[1]=='\0'||ch[1]=='\n')+returnch-param;+++ch;+}+}++staticint__initcma_map_param(char*param)+{+ssize_tlen;++pr_debug("param: map: %s\n",param);++len=cma_map_validate(param);+if(len<0)+returnlen;++cma_map=param;+cma_map_length=len;+return0;+}++#if defined CONFIG_CMA_CMDLINE++early_param("cma.map",cma_map_param);++#endif++++/************************* Early regions *************************/++structlist_headcma_early_regions__initdata=+LIST_HEAD_INIT(cma_early_regions);++#ifdef CONFIG_CMA_CMDLINE++/*+*regions-attr::=[regions[';']]+*regions::=region[';'regions]+*+*region::=['-']reg-name+*'='size+*['@'start]+*['/'alignment]+*+*SeeDocumentation/contiguous-memory.txtfordetails.+*+*Example:+*cma=reg1=64M;reg2=32M@0x100000;reg3=64M/1M+*+*/++#define NUMPARSE(cond_ch, type, cond) ({ \+unsignedlonglongv=0;\+if(*param==(cond_ch)){\+constchar*constmsg=param+1;\+v=memparse(msg,¶m);\+if(!v||v>~(type)0||!(cond)){\+pr_err("param: invalid value near %s\n",msg);\+ret=-EINVAL;\+break;\+}\+}\+v;\+})++staticint__initcma_param_parse(char*param)+{+staticstructcma_regionregions[16];++size_tleft=ARRAY_SIZE(regions);+structcma_region*reg=regions;+intret=0;++pr_debug("param: %s\n",param);++for(;*param;++reg){+dma_addr_tstart,alignment;+size_tsize;++if(unlikely(!--left)){+pr_err("param: too many early regions\n");+return-ENOSPC;+}++/* Parse name */+reg->name=param;+param=strchr(param,'=');+if(!param||param==reg->name){+pr_err("param: expected \"<name>=\" near %s\n",+reg->name);+ret=-EINVAL;+break;+}+*param='\0';++/* Parse numbers */+size=NUMPARSE('\0',size_t,true);+start=NUMPARSE('@',dma_addr_t,true);+alignment=NUMPARSE('/',dma_addr_t,(v&(v-1))==0);++alignment=max(alignment,(dma_addr_t)PAGE_SIZE);+start=ALIGN(start,alignment);+size=PAGE_ALIGN(size);+if(start+size<start){+pr_err("param: invalid start, size combination\n");+ret=-EINVAL;+break;+}++/* Go to next */+if(*param==';'){+*param='\0';+++param;+}elseif(*param){+pr_err("param: expecting ';' or end of parameter near %s\n",+param);+ret=-EINVAL;+break;+}++/* Add */+reg->size=size;+reg->start=start;+reg->alignment=alignment;+reg->copy_name=1;++list_add_tail(®->list,&cma_early_regions);++pr_debug("param: registering early region %s (%p@%p/%p)\n",+reg->name,(void*)reg->size,(void*)reg->start,+(void*)reg->alignment);+}++returnret;+}+early_param("cma",cma_param_parse);++#undef NUMPARSE++#endif+++int__init__must_checkcma_early_region_register(structcma_region*reg)+{+dma_addr_tstart,alignment;+size_tsize;++if(reg->alignment&(reg->alignment-1))+return-EINVAL;++alignment=max(reg->alignment,(dma_addr_t)PAGE_SIZE);+start=ALIGN(reg->start,alignment);+size=PAGE_ALIGN(reg->size);++if(start+size<start)+return-EINVAL;++reg->size=size;+reg->start=start;+reg->alignment=alignment;++list_add_tail(®->list,&cma_early_regions);++pr_debug("param: registering early region %s (%p@%p/%p)\n",+reg->name,(void*)reg->size,(void*)reg->start,+(void*)reg->alignment);++return0;+}++++/************************* Regions ******************************/++/* List of all regions. Named regions are kept before unnamed. */+staticLIST_HEAD(cma_regions);++#define cma_foreach_region(reg) \+list_for_each_entry(reg,&cma_regions,list)++int__must_checkcma_region_register(structcma_region*reg)+{+constchar*name;+structcma_region*r;+char*ch=NULL;+intret=0;++if(!reg->size||reg->start+reg->size<reg->start)+return-EINVAL;++reg->used=0;+reg->registered=0;++/* Copy name */+name=reg->name;+if(reg->copy_name&&(reg->name)){+size_tname_size;++name_size=reg->name?strlen(reg->name)+1:0;++ch=kmalloc(name_size,GFP_KERNEL);+if(!ch){+pr_err("%s: not enough memory to allocate name\n",+reg->name?:"(private)");+return-ENOMEM;+}++if(name_size){+memcpy(ch,reg->name,name_size);+name=ch;+ch+=name_size;+}+}++mutex_lock(&cma_mutex);++/* Don't let regions overlap */+cma_foreach_region(r)+if(r->start+r->size>reg->start&&+r->start<reg->start+reg->size){+ret=-EADDRINUSE;+gotodone;+}++reg->name=name;+reg->registered=1;+reg->cma=cma_create(reg->start,reg->size,reg->alignment,false);++if(IS_ERR(reg->cma)){+pr_err("error, cma create failed: %d\n",-(int)reg->cma);+ret=PTR_ERR(reg->cma);+gotodone;+}+pr_info("created cma %p\n",reg->cma);+ch=NULL;++/*+*Keepnamedatthebeginningandunnamed(private)atthe+*end.Thishelpsintraversalwhennamedregionislooked+*for.+*/+if(name)+list_add(®->list,&cma_regions);+else+list_add_tail(®->list,&cma_regions);++done:+mutex_unlock(&cma_mutex);++pr_debug("%s: region %sregistered\n",+reg->name?:"(private)",ret?"not ":"");+kfree(ch);++returnret;+}+EXPORT_SYMBOL_GPL(cma_region_register);++staticstructcma_region*__must_check+__cma_region_find(constchar**namep)+{+structcma_region*reg;+constchar*ch,*name;+size_tn;++ch=*namep;+while(*ch&&*ch!=','&&*ch!=';')+++ch;+name=*namep;+*namep=*ch==','?ch+1:ch;+n=ch-name;++/*+*Namedregionsarekeptinfrontofunnamedsoifwe+*encounterunnamedregionwecanstop.+*/+cma_foreach_region(reg)+if(!reg->name)+break;+elseif(!strncmp(name,reg->name,n)&&!reg->name[n])+returnreg;++returnNULL;+}++/************************* Initialise CMA *************************/++int__initcma_set_defaults(structcma_region*regions,constchar*map)+{+if(map){+intret=cma_map_param((char*)map);+if(unlikely(ret<0))+returnret;+}++if(!regions)+return0;++for(;regions->size;++regions){+intret=cma_early_region_register(regions);+if(unlikely(ret<0))+returnret;+}++return0;+}+++int__initcma_early_region_reserve(structcma_region*reg)+{+intret;++if(!reg->size||(reg->alignment&(reg->alignment-1))||+reg->reserved)+return-EINVAL;++ret=cma_reserve(reg->start,reg->size,reg->alignment,true);++if(ret>=0)+reg->start=ret;++returnret;+}++void__initcma_early_regions_reserve(int(*reserve)(structcma_region*reg))+{+structcma_region*reg;++pr_debug("init: reserving early regions\n");++if(!reserve)+reserve=cma_early_region_reserve;++list_for_each_entry(reg,&cma_early_regions,list){+if(reg->reserved){+/* nothing */+}elseif(reserve(reg)>=0){+pr_debug("init: %s: reserved %p@%p\n",+reg->name?:"(private)",+(void*)reg->size,(void*)reg->start);+reg->reserved=1;+}else{+pr_warn("init: %s: unable to reserve %p@%p/%p\n",+reg->name?:"(private)",+(void*)reg->size,(void*)reg->start,+(void*)reg->alignment);+}+}+}+++staticint__initcma_init(void)+{+structcma_region*reg,*n;++pr_debug("init: initialising\n");++if(cma_map){+char*val=kmemdup(cma_map,cma_map_length+1,GFP_KERNEL);+cma_map=val;+if(!val)+return-ENOMEM;+val[cma_map_length]='\0';+}++list_for_each_entry_safe(reg,n,&cma_early_regions,list){+INIT_LIST_HEAD(®->list);+/*+*Wedon'tcareiftherewasanerror.It'sapity+*butthere'snotmuchwecandoaboutitanyway.+*Iftheerrorisonaregionthatwasparsedfrom+*commandlinethenitwillstayandwasteabitof+*space;ifitwasregisteredusing+*cma_early_region_register()it'scaller's+*responsibilitytodosomethingaboutit.+*/+if(reg->reserved&&cma_region_register(reg)<0)+/* ignore error */;+}++INIT_LIST_HEAD(&cma_early_regions);++return0;+}+/*+*Wewanttobeinitialisedearlierthanmodule_init/__initcallso+*thatdriversthatwanttograbmemory@boottimewillgetCMA+*ready.subsys_initcall()seemsearlyenoughandnottooearlyat+*thesametime.+*/+subsys_initcall(cma_init);++/************************* The Device API *************************/++staticconstchar*__must_check+__cma_where_from(conststructdevice*dev,constchar*type);+++/* Allocate. */++staticstructcm*__must_check+__cma_alloc_from_region(structcma_region*reg,+size_tsize,dma_addr_talignment)+{+structcm*cm;++pr_debug("allocate %p/%p from %s\n",+(void*)size,(void*)alignment,+reg?reg->name?:"(private)":"(null)");++if(!reg)+returnERR_PTR(-ENOMEM);++cm=cm_alloc(reg->cma,size,alignment);+if(IS_ERR(cm)){+pr_err("failed to allocate\n");+returnERR_PTR(-ENOMEM);+}+returncm;+}++structcm*__must_check+cma_alloc_from_region(structcma_region*reg,+size_tsize,dma_addr_talignment)+{+structcm*cm;++pr_debug("allocate %p/%p from %s\n",+(void*)size,(void*)alignment,+reg?reg->name?:"(private)":"(null)");++if(!size||alignment&(alignment-1)||!reg)+returnERR_PTR(-EINVAL);++mutex_lock(&cma_mutex);++cm=reg->registered?+__cma_alloc_from_region(reg,PAGE_ALIGN(size),+max(alignment,(dma_addr_t)PAGE_SIZE)):+ERR_PTR(-EINVAL);++mutex_unlock(&cma_mutex);++returncm;+}+EXPORT_SYMBOL_GPL(cma_alloc_from_region);++structcm*__must_check+__cma_alloc(conststructdevice*dev,constchar*type,+dma_addr_tsize,dma_addr_talignment)+{+structcma_region*reg;+constchar*from;+structcm*cm;++if(dev)+pr_debug("allocate %p/%p for %s/%s\n",+(void*)size,(void*)alignment,+dev_name(dev),type?:"");++if(!size||alignment&(alignment-1))+returnERR_PTR(-EINVAL);++size=PAGE_ALIGN(size);+if(alignment<PAGE_SIZE)+alignment=PAGE_SIZE;++mutex_lock(&cma_mutex);++from=__cma_where_from(dev,type);+if(unlikely(IS_ERR(from))){+cm=ERR_PTR(PTR_ERR(from));+gotodone;+}++pr_debug("allocate %p/%p from one of %s\n",+(void*)size,(void*)alignment,from);++while(*from&&*from!=';'){+reg=__cma_region_find(&from);+cm=__cma_alloc_from_region(reg,size,alignment);+if(!IS_ERR(cm))+gotodone;+}++pr_debug("not enough memory\n");+cm=ERR_PTR(-ENOMEM);++done:+mutex_unlock(&cma_mutex);++returncm;+}+EXPORT_SYMBOL_GPL(__cma_alloc);+++/* Query information about regions. */+staticvoid__cma_info_add(structcma_info*infop,structcma_region*reg)+{+infop->total_size+=reg->size;+if(infop->lower_bound>reg->start)+infop->lower_bound=reg->start;+if(infop->upper_bound<reg->start+reg->size)+infop->upper_bound=reg->start+reg->size;+++infop->count;+}++int+__cma_info(structcma_info*infop,conststructdevice*dev,constchar*type)+{+structcma_infoinfo={~(dma_addr_t)0,0,0,0,0};+structcma_region*reg;+constchar*from;+intret;++if(unlikely(!infop))+return-EINVAL;++mutex_lock(&cma_mutex);++from=__cma_where_from(dev,type);+if(IS_ERR(from)){+ret=PTR_ERR(from);+info.lower_bound=0;+gotodone;+}++while(*from&&*from!=';'){+reg=__cma_region_find(&from);+if(reg)+__cma_info_add(&info,reg);+}++ret=0;+done:+mutex_unlock(&cma_mutex);++memcpy(infop,&info,sizeofinfo);+returnret;+}+EXPORT_SYMBOL_GPL(__cma_info);+++/* Freeing. */+intcma_free(structcm*cm)+{+mutex_lock(&cma_mutex);++if(cm)+pr_debug("free(%p): freed\n",(void*)cm);++cm_free(cm);+mutex_unlock(&cma_mutex);+return0;+}+EXPORT_SYMBOL_GPL(cma_free);+++/************************* Miscellaneous *************************/++/*+*s::=rules+*rules::=rule[';'rules]+*rule::=patterns'='regions+*patterns::=pattern[','patterns]+*regions::=REG-NAME[','regions]+*pattern::=dev-pattern['/'TYPE-NAME]|'/'TYPE-NAME+*/+staticconstchar*__must_check+__cma_where_from(conststructdevice*dev,constchar*type)+{+/*+*Thisfunctionmatchesthepatternfromthemapattribute+*againsgivendevicenameandtype.Typemaybeofcourse+*NULLoranemtpystring.+*/++constchar*s,*name;+intname_matched=0;++/*+*IfdevisNULLwewerecalledinalternativeformwhere+*typeisthefromstring.Allwehavetodoisreturnit.+*/+if(!dev)+returntype?:ERR_PTR(-EINVAL);++if(!cma_map)+returnERR_PTR(-ENOENT);++name=dev_name(dev);+if(WARN_ON(!name||!*name))+returnERR_PTR(-EINVAL);++if(!type)+type="common";++/*+*Nowwegothroughtthecma_mapattribute.+*/+for(s=cma_map;*s;++s){+constchar*c;++/*+*Ifthepatternstartswithaslash,thedevicepartofthe+*patternmatchesifitmatchedpreviously.+*/+if(*s=='/'){+if(!name_matched)+gotolook_for_next;+gotomatch_type;+}++/*+*Wearenowtryingtomatchthedevicename.Thisalso+*updatesthename_matchedvariable.If,whilereadingthe+*spec,weecnountercommaitmeansthatthepatterndoesnot+*matchandweneedtostartoverwithanotherpattern(the+*oneaftherthecomma).Ifweencounterequalsignweneed+*tostartoverwithanotherrule.Ifthereisacharacter+*thatdoesnotmatch,weneettolookforacomma(toget+*anotherpattern)orsemicolon(togetanotherrule)andtry+*againifthereisonesomewhere.+*/++name_matched=0;++for(c=name;*s!='*'&&*c;++c,++s)+if(*s=='=')+gotonext_rule;+elseif(*s==',')+gotonext_pattern;+elseif(*s!='?'&&*c!=*s)+gotolook_for_next;+if(*s=='*')+++s;++name_matched=1;++/*+*Nowweneedtomatchthetypepartofthepattern.Ifthe+*patternismissingitwematchonlyiftypepointstoan+*emptystring.Otherwisewytrytomatchitjustlikename.+*/+if(*s=='/'){+match_type:/* s points to '/' */+++s;++for(c=type;*s&&*c;++c,++s)+if(*s=='=')+gotonext_rule;+elseif(*s==',')+gotonext_pattern;+elseif(*c!=*s)+gotolook_for_next;+}++/* Return the string behind the '=' sign of the rule. */+if(*s=='=')+returns+1;+elseif(*s==',')+returnstrchr(s,'=')+1;++/* Pattern did not match */++look_for_next:+do{+++s;+}while(*s!=','&&*s!='=');+if(*s==',')+continue;++next_rule:/* s points to '=' */+s=strchr(s,';');+if(!s)+break;++next_pattern:+continue;+}++returnERR_PTR(-ENOENT);+}
From: Dave Hansen <hidden> Date: 2011-03-31 15:58:15
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
+unsigned long alloc_contig_freed_pages(unsigned long start, unsigned long end,
+ gfp_t flag)
+{
+ unsigned long pfn = start, count;
+ struct page *page;
+ struct zone *zone;
+ int order;
+
+ VM_BUG_ON(!pfn_valid(start));
This seems kinda mean. Could we return an error? I understand that
this is largely going to be an early-boot thing, but surely trying to
punt on crappy input beats a full-on BUG().
if (!pfn_valid(start))
return -1;
If start->end happens to span the end of a zone, I believe this will
jump out of the zone. It will still be pfn_valid(), but potentially not
in the same zone.
That will break on SPARSEMEM. You potentially need to revalidate the
pfn->page mapping on every MAX_ORDER pfn change. It's easiest to just
do pfn_to_page() on each loop.
+
+ spin_unlock_irq(&zone->lock);
+void free_contig_pages(struct page *page, int nr_pages)
+{
+ for (; nr_pages; --nr_pages, ++page)
+ __free_page(page);
+}
Can't help but notice that this resembles a bit of a patch I posted last
week:
http://www.spinics.net/lists/linux-mm/msg16364.html
We'll have to make sure we only have one copy of this in the end.
-- Dave
From: Dave Hansen <hidden> Date: 2011-03-31 16:02:56
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
+ ret = 0;
+ while (!PageBuddy(pfn_to_page(start & (~0UL << ret))))
+ if (WARN_ON(++ret >= MAX_ORDER))
+ return -EINVAL;
Holy cow, that's dense. Is there really no more straightforward way to
do that?
In any case, please pull the ++ret bit out of the WARN_ON(). Some
people like to do:
#define WARN_ON(...) do{}while(0)
to save space on some systems.
-- Dave
From: Michal Nazarewicz <hidden> Date: 2011-03-31 16:26:52
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+ ret = 0;
+ while (!PageBuddy(pfn_to_page(start & (~0UL << ret))))
+ if (WARN_ON(++ret >= MAX_ORDER))
+ return -EINVAL;
On Thu, 31 Mar 2011 18:02:41 +0200, Dave Hansen wrote:
Holy cow, that's dense. Is there really no more straightforward way to
do that?
Which part exactly is dense? What would be qualify as a more
straightforward way?
In any case, please pull the ++ret bit out of the WARN_ON(). Some
people like to do:
#define WARN_ON(...) do{}while(0)
to save space on some systems.
I don't think that's the case. Even if WARN_ON() decides not to print
a warning, it will still return the value of the argument. If not,
a lot of code will brake.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michal "mina86" Nazarewicz (o o)
ooo +-----<email/xmpp: mnazarewicz@google.com>-----ooO--(_)--Ooo--
These names are a wee bit lacking. Care to give them proper names that
might let a reader figure out how the "_" makes the variable different
from its nearly-identical twin?
-- Dave
From: Steven Rostedt <rostedt@goodmis.org> Date: 2011-03-31 19:24:36
On Thu, Mar 31, 2011 at 08:58:03AM -0700, Dave Hansen wrote:
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+unsigned long alloc_contig_freed_pages(unsigned long start, unsigned long end,
+ gfp_t flag)
+{
+ unsigned long pfn = start, count;
+ struct page *page;
+ struct zone *zone;
+ int order;
+
+ VM_BUG_ON(!pfn_valid(start));
This seems kinda mean. Could we return an error? I understand that
this is largely going to be an early-boot thing, but surely trying to
punt on crappy input beats a full-on BUG().
if (!pfn_valid(start))
return -1;
But still keep the warning?
if (WARN_ON(!pfn_valid(start))
return -1;
-- Steve
From: Steven Rostedt <rostedt@goodmis.org> Date: 2011-03-31 19:26:55
On Thu, Mar 31, 2011 at 09:02:41AM -0700, Dave Hansen wrote:
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+ ret = 0;
+ while (!PageBuddy(pfn_to_page(start & (~0UL << ret))))
+ if (WARN_ON(++ret >= MAX_ORDER))
+ return -EINVAL;
Holy cow, that's dense. Is there really no more straightforward way to
do that?
In any case, please pull the ++ret bit out of the WARN_ON(). Some
people like to do:
#define WARN_ON(...) do{}while(0)
to save space on some systems.
That should be fixed, as the if (WARN_ON()) has become a standard in
most of the kernel. Removing WARN_ON() should be:
#define WARN_ON(x) ({0;})
But I agree, that there should be no "side effects" inside a WARN_ON(),
which that "++ret" is definitely one.
-- Steve
From: Steven Rostedt <rostedt@goodmis.org> Date: 2011-03-31 19:28:27
On Thu, Mar 31, 2011 at 06:26:45PM +0200, Michal Nazarewicz wrote:
quoted
In any case, please pull the ++ret bit out of the WARN_ON(). Some
people like to do:
#define WARN_ON(...) do{}while(0)
to save space on some systems.
I don't think that's the case. Even if WARN_ON() decides not to print
a warning, it will still return the value of the argument. If not,
a lot of code will brake.
WARN_ON() should never do anything but test. That ret++ does not belong
inside the WARN_ON() condition. If there are other locations in the
kernel that do that, then those locations need to be fixed.
-- Steve
From: Michal Nazarewicz <hidden> Date: 2011-03-31 19:52:33
On Thu, 31 Mar 2011 21:28:21 +0200, Steven Rostedt wrote:
WARN_ON() should never do anything but test. That ret++ does not belong
inside the WARN_ON() condition. If there are other locations in the
kernel that do that, then those locations need to be fixed.
Testing implies evaluating, so if we allow:
if (++i == end) { /* ... */ }
I see no reason why not to allow:
if (WARN_ON(++i == end)) { /* ... */ }
In both cases the condition is tested.
quoted
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+ ret = 0;
+ while (!PageBuddy(pfn_to_page(start & (~0UL << ret))))
+ if (WARN_ON(++ret >= MAX_ORDER))
+ return -EINVAL;
On Thu, Mar 31, 2011 at 09:02:41AM -0700, Dave Hansen wrote:
quoted
In any case, please pull the ++ret bit out of the WARN_ON(). Some
people like to do:
#define WARN_ON(...) do{}while(0)
to save space on some systems.
On Thu, 31 Mar 2011 21:26:50 +0200, Steven Rostedt wrote:
That should be fixed, as the if (WARN_ON()) has become a standard in
most of the kernel. Removing WARN_ON() should be:
#define WARN_ON(x) ({0;})
But I agree, that there should be no "side effects" inside a WARN_ON(),
which that "++ret" is definitely one.
Thus I don't really agree with this point.
At any rate, I don't really care.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michal "mina86" Nazarewicz (o o)
ooo +-----<email/xmpp: mnazarewicz@google.com>-----ooO--(_)--Ooo--
From: Dave Hansen <hidden> Date: 2011-03-31 20:28:53
On Thu, 2011-03-31 at 18:26 +0200, Michal Nazarewicz wrote:
quoted
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+ ret = 0;
+ while (!PageBuddy(pfn_to_page(start & (~0UL << ret))))
+ if (WARN_ON(++ret >= MAX_ORDER))
+ return -EINVAL;
On Thu, 31 Mar 2011 18:02:41 +0200, Dave Hansen wrote:
quoted
Holy cow, that's dense. Is there really no more straightforward way to
do that?
Which part exactly is dense? What would be qualify as a more
straightforward way?
I'm still not 100% sure what it's trying to do. It looks like it
attempts to check all of "start"'s buddy pages.
unsigned long find_buddy(unsigned long pfn, int buddy)
{
unsigned long page_idx = pfn & ((1 << MAX_ORDER) - 1); // You had a macro for this I think
unsigned long buddy_idx = __find_buddy_index(page_idx, order);
return page_idx + buddy_idx;
}
Is something like this equivalent?
int order;
for (order = 0; order <= MAX_ORDER; order++) {
unsigned long buddy_pfn = find_buddy(start, order);
struct page *buddy = pfn_to_page(buddy_pfn);
if (PageBuddy(buddy)
break;
WARN();
return -EINVAL;
}
I'm wondering also if you can share some code with __rmqueue().
quoted
In any case, please pull the ++ret bit out of the WARN_ON(). Some
people like to do:
#define WARN_ON(...) do{}while(0)
to save space on some systems.
I don't think that's the case. Even if WARN_ON() decides not to print
a warning, it will still return the value of the argument. If not,
a lot of code will brake.
Bah, sorry. I'm confusing WARN_ON() and WARN().
-- Dave
From: Dave Hansen <hidden> Date: 2011-03-31 20:33:48
On Thu, 2011-03-31 at 15:24 -0400, Steven Rostedt wrote:
On Thu, Mar 31, 2011 at 08:58:03AM -0700, Dave Hansen wrote:
quoted
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+unsigned long alloc_contig_freed_pages(unsigned long start, unsigned long end,
+ gfp_t flag)
+{
+ unsigned long pfn = start, count;
+ struct page *page;
+ struct zone *zone;
+ int order;
+
+ VM_BUG_ON(!pfn_valid(start));
This seems kinda mean. Could we return an error? I understand that
this is largely going to be an early-boot thing, but surely trying to
punt on crappy input beats a full-on BUG().
if (!pfn_valid(start))
return -1;
But still keep the warning?
if (WARN_ON(!pfn_valid(start))
return -1;
Sure. You might also want to make sure you're respecting __GFP_NOWARN
if you're going to do that, or maybe just warn once per boot.
-- Dave
From: Michal Nazarewicz <hidden> Date: 2011-03-31 21:09:48
On Thu, 31 Mar 2011 17:58:03 +0200, Dave Hansen [off-list ref]
wrote:
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+unsigned long alloc_contig_freed_pages(unsigned long start, unsigned
long end,
+ gfp_t flag)
+{
+ unsigned long pfn = start, count;
+ struct page *page;
+ struct zone *zone;
+ int order;
+
+ VM_BUG_ON(!pfn_valid(start));
This seems kinda mean. Could we return an error? I understand that
this is largely going to be an early-boot thing, but surely trying to
punt on crappy input beats a full-on BUG().
Actually, I would have to check but I think that the usage of this function
(in this patchset) is that the caller expects the function to succeed. It
is
quite a low-level function so before running it a lot of preparation is
needed
and the caller must make sure that several conditions are met. I don't
really
see advantage of returning a value rather then BUG()ing.
Also, CMA does not call this function at boot time.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michal "mina86" Nazarewicz (o o)
ooo +-----<email/xmpp: mnazarewicz@google.com>-----ooO--(_)--Ooo--
From: Dave Hansen <hidden> Date: 2011-03-31 21:14:46
On Thu, 2011-03-31 at 23:09 +0200, Michal Nazarewicz wrote:
On Thu, 31 Mar 2011 17:58:03 +0200, Dave Hansen [off-list ref]
wrote:
quoted
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+unsigned long alloc_contig_freed_pages(unsigned long start, unsigned
long end,
+ gfp_t flag)
+{
+ unsigned long pfn = start, count;
+ struct page *page;
+ struct zone *zone;
+ int order;
+
+ VM_BUG_ON(!pfn_valid(start));
This seems kinda mean. Could we return an error? I understand that
this is largely going to be an early-boot thing, but surely trying to
punt on crappy input beats a full-on BUG().
Actually, I would have to check but I think that the usage of this function
(in this patchset) is that the caller expects the function to succeed. It
is quite a low-level function so before running it a lot of preparation is
needed and the caller must make sure that several conditions are met. I don't
really see advantage of returning a value rather then BUG()ing.
Also, CMA does not call this function at boot time.
We BUG_ON() in bootmem. Basically if we try to allocate an early-boot
structure and fail, we're screwed. We can't keep running without an
inode hash, or a mem_map[].
This looks like it's going to at least get partially used in drivers, at
least from the examples. Are these kinds of things that, if the driver
fails to load, that the system is useless and hosed? Or, is it
something where we might limp along to figure out what went wrong before
we reboot?
-- Dave
From: Michal Nazarewicz <hidden> Date: 2011-03-31 21:17:57
On Thu, 31 Mar 2011 22:28:42 +0200, Dave Hansen [off-list ref]
wrote:
On Thu, 2011-03-31 at 18:26 +0200, Michal Nazarewicz wrote:
quoted
quoted
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+ ret = 0;
+ while (!PageBuddy(pfn_to_page(start & (~0UL << ret))))
+ if (WARN_ON(++ret >= MAX_ORDER))
+ return -EINVAL;
On Thu, 31 Mar 2011 18:02:41 +0200, Dave Hansen wrote:
quoted
Holy cow, that's dense. Is there really no more straightforward way
to
quoted
do that?
Which part exactly is dense? What would be qualify as a more
straightforward way?
I'm still not 100% sure what it's trying to do. It looks like it
attempts to check all of "start"'s buddy pages.
No. I'm going up through parents. This is because even though start
falls in a free block (ie. one that page allocator tracks), the actual
page that is in buddy system is larger then start and this loop looks
for beginning of that page.
int order;
for (order = 0; order <= MAX_ORDER; order++) {
unsigned long buddy_pfn = find_buddy(start, order);
struct page *buddy = pfn_to_page(buddy_pfn);
if (PageBuddy(buddy)
break;
WARN();
return -EINVAL;
}
The WARN() and return would have to be outside of the loop and, as I
described, instead of find_buddy() something like find_parent() would
have to be used.
I'm wondering also if you can share some code with __rmqueue().
Doubtful since start does not (have to) point to a page that is tracked
by page allocator but a page inside such a page.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michal "mina86" Nazarewicz (o o)
ooo +-----<email/xmpp: mnazarewicz@google.com>-----ooO--(_)--Ooo--
From: Michal Nazarewicz <hidden> Date: 2011-03-31 22:18:16
On Thu, 2011-03-31 at 15:16 +0200, Marek Szyprowski wrote:
quoted
+unsigned long alloc_contig_freed_pages(unsigned long start, unsigned
long end,
+ gfp_t flag)
+{
+ unsigned long pfn = start, count;
+ struct page *page;
+ struct zone *zone;
+ int order;
+
+ VM_BUG_ON(!pfn_valid(start));
On Thu, 31 Mar 2011 23:14:38 +0200, Dave Hansen wrote:
We BUG_ON() in bootmem. Basically if we try to allocate an early-boot
structure and fail, we're screwed. We can't keep running without an
inode hash, or a mem_map[].
This looks like it's going to at least get partially used in drivers, at
least from the examples. Are these kinds of things that, if the driver
fails to load, that the system is useless and hosed? Or, is it
something where we might limp along to figure out what went wrong before
we reboot?
Bug in the above place does not mean that we could not allocate memory. It
means caller is broken.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michal "mina86" Nazarewicz (o o)
ooo +-----<email/xmpp: mnazarewicz@google.com>-----ooO--(_)--Ooo--
From: Dave Hansen <hidden> Date: 2011-03-31 22:27:06
On Fri, 2011-04-01 at 00:18 +0200, Michal Nazarewicz wrote:
On Thu, 31 Mar 2011 23:14:38 +0200, Dave Hansen wrote:
quoted
We BUG_ON() in bootmem. Basically if we try to allocate an early-boot
structure and fail, we're screwed. We can't keep running without an
inode hash, or a mem_map[].
This looks like it's going to at least get partially used in drivers, at
least from the examples. Are these kinds of things that, if the driver
fails to load, that the system is useless and hosed? Or, is it
something where we might limp along to figure out what went wrong before
we reboot?
Bug in the above place does not mean that we could not allocate memory. It
means caller is broken.
Could you explain that a bit?
Is this a case where a device is mapped to a very *specific* range of
physical memory and no where else? What are the reasons for not marking
it off limits at boot? I also saw some bits of isolation and migration
in those patches. Can't the migration fail?
-- Dave
From: Michal Nazarewicz <hidden> Date: 2011-03-31 22:51:38
On Fri, 01 Apr 2011 00:26:51 +0200, Dave Hansen [off-list ref]
wrote:
On Fri, 2011-04-01 at 00:18 +0200, Michal Nazarewicz wrote:
quoted
On Thu, 31 Mar 2011 23:14:38 +0200, Dave Hansen wrote:
quoted
We BUG_ON() in bootmem. Basically if we try to allocate an early-boot
structure and fail, we're screwed. We can't keep running without an
inode hash, or a mem_map[].
This looks like it's going to at least get partially used in drivers,
at
quoted
least from the examples. Are these kinds of things that, if the
driver
quoted
fails to load, that the system is useless and hosed? Or, is it
something where we might limp along to figure out what went wrong
before
quoted
we reboot?
Bug in the above place does not mean that we could not allocate
memory. It means caller is broken.
Could you explain that a bit?
Is this a case where a device is mapped to a very *specific* range of
physical memory and no where else? What are the reasons for not marking
it off limits at boot? I also saw some bits of isolation and migration
in those patches. Can't the migration fail?
The function is called from alloc_contig_range() (see patch 05/12) which
makes sure that the PFN is valid. Situation where there is not enough
space is caught earlier in alloc_contig_range().
alloc_contig_freed_pages() must be given a valid PFN range such that all
the pages in that range are free (as in are within the region tracked by
page allocator) and of MIGRATETYPE_ISOLATE so that page allocator won't
touch them.
That's why invalid PFN is a bug in the caller and not an exception that
has to be handled.
Also, the function is not called during boot time. It is called while
system is already running.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michal "mina86" Nazarewicz (o o)
ooo +-----<email/xmpp: mnazarewicz@google.com>-----ooO--(_)--Ooo--
From: Dave Hansen <hidden> Date: 2011-04-01 14:03:46
On Fri, 2011-04-01 at 00:51 +0200, Michal Nazarewicz wrote:
On Fri, 01 Apr 2011 00:26:51 +0200, Dave Hansen [off-list ref]
wrote:
quoted
quoted
Bug in the above place does not mean that we could not allocate
memory. It means caller is broken.
Could you explain that a bit?
Is this a case where a device is mapped to a very *specific* range of
physical memory and no where else? What are the reasons for not marking
it off limits at boot? I also saw some bits of isolation and migration
in those patches. Can't the migration fail?
The function is called from alloc_contig_range() (see patch 05/12) which
makes sure that the PFN is valid. Situation where there is not enough
space is caught earlier in alloc_contig_range().
alloc_contig_freed_pages() must be given a valid PFN range such that all
the pages in that range are free (as in are within the region tracked by
page allocator) and of MIGRATETYPE_ISOLATE so that page allocator won't
touch them.
OK, so it really is a low-level function only. How about a comment that
explicitly says this? "Only called from $FOO with the area already
isolated." It probably also deserves an __ prefix.
That's why invalid PFN is a bug in the caller and not an exception that
has to be handled.
Also, the function is not called during boot time. It is called while
system is already running.
What kind of success have you had running this in practice? I'd be
worried that some silly task or a sticky dentry would end up in the
range that you want to allocate in.
-- Dave
From: Michal Nazarewicz <hidden> Date: 2011-04-04 13:15:14
On Fri, 2011-04-01 at 00:51 +0200, Michal Nazarewicz wrote:
quoted
The function is called from alloc_contig_range() (see patch 05/12) which
makes sure that the PFN is valid. Situation where there is not enough
space is caught earlier in alloc_contig_range().
alloc_contig_freed_pages() must be given a valid PFN range such that all
the pages in that range are free (as in are within the region tracked by
page allocator) and of MIGRATE_ISOLATE so that page allocator won't
touch them.
On Fri, 01 Apr 2011 16:03:16 +0200, Dave Hansen wrote:
OK, so it really is a low-level function only. How about a comment that
explicitly says this? "Only called from $FOO with the area already
isolated." It probably also deserves an __ prefix.
Yes, it's not really for general use. Comment may indeed be useful here.
quoted
That's why invalid PFN is a bug in the caller and not an exception that
has to be handled.
Also, the function is not called during boot time. It is called while
system is already running.
What kind of success have you had running this in practice? I'd be
worried that some silly task or a sticky dentry would end up in the
range that you want to allocate in.
I'm not sure what you are asking.
The function requires the range to be marked as MIGRATE_ISOLATE and all
pages being free, so nothing can be allocated there while the function
is running.
If you are asking about CMA in general, the range that CMA uses is marked
as MIGRATE_CMA (a new migrate type) which means that only MIGRATE_MOVABLE
pages can be allocated there. This means, that in theory, if there is
enough memory the pages can always be moved out of the region. At leasts
that's my understanding of the type. If this is correct, the allocation
should always succeed provided enough memory for the pages within the
region to be moved to is available.
As of practice, I have run some simple test to see if the code works and
they succeeded. Also, Marek has run some test with actual hardware and
those worked well as well (but I'll let Marek talk about any details).
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michal "mina86" Nazarewicz (o o)
ooo +-----<email/xmpp: mnazarewicz@google.com>-----ooO--(_)--Ooo--
From: Marek Szyprowski <m.szyprowski@samsung.com> Date: 2011-04-05 07:24:49
Hello,
On Monday, April 04, 2011 3:15 PM Micha? Nazarewicz wrote:
quoted
What kind of success have you had running this in practice? I'd be
worried that some silly task or a sticky dentry would end up in the
range that you want to allocate in.
I'm not sure what you are asking.
The function requires the range to be marked as MIGRATE_ISOLATE and all
pages being free, so nothing can be allocated there while the function
is running.
If you are asking about CMA in general, the range that CMA uses is marked
as MIGRATE_CMA (a new migrate type) which means that only MIGRATE_MOVABLE
pages can be allocated there. This means, that in theory, if there is
enough memory the pages can always be moved out of the region. At leasts
that's my understanding of the type. If this is correct, the allocation
should always succeed provided enough memory for the pages within the
region to be moved to is available.
As of practice, I have run some simple test to see if the code works and
they succeeded. Also, Marek has run some test with actual hardware and
those worked well as well (but I'll let Marek talk about any details).
We did the tests with real multimedia drivers - video codec and video
converter (s5p-mfc and s5p-fimc). These drivers allocate large contiguous
buffers for video data. The allocation is performed when driver is opened
by user space application.
First we consumed system memory by running a set of simple applications
that just did some malloc() and filled memory with random pattern to consume
free pages. Then some of that memory has been freed and we ran the video
decoding application. Multimedia drivers successfully managed to allocate
required contiguous buffers from MIGRATE_CMA ranges.
The tests have been performed with different system usage patterns (malloc(),
heavy filesystem load, anonymous memory mapping). In all these cases CMA
worked surprisingly good allowing the drivers to allocate the required
contiguous buffers.
Best regards
--
Marek Szyprowski
Samsung Poland R&D Center