Skip to contentSkip to conversation

memory defragmentation to satisfy high order allocations

RFC In review Marcelo Tosatti 9 participants 54 messages Oct 1, 2004 – Oct 12, 2004

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 memory defragmentation to satisfy high order allocations

Hi fellows,

So I've been playing with memory defragmentation for the last couple of weeks.

The following patch implements a "coalesce_memory()" function which takes "zone" and "order" as a parameter.

It tries to move enough physically nearby pages to form a free area of "order" size.

It does that by checking whether the page can be moved, allocating a new page, unmapping the pte's to it, copying data to new page, remapping the ptes, and reinserting the page on the radix/LRU.

Its very uncomplete yet - for one SMP concurrent radix lookups will screw file page unmapping (swapcache lookup should be safe), and lots of other buggies inside. For example it doesnt re establishes pte's once it has unmapped them.

I'm working on those.

But it works fine on UP (for a few minutes :)), and easily creates large physically contiguous areas of memory.

With such a thing in place we can build a mechanism for kswapd (or a separate kernel thread, if needed) to notice when we are low on high order pages, and use the coalescing algorithm instead blindly freeing unique pages from LRU in the hope to build large physically contiguous memory areas.

Comments appreciated.

Lots of this has been copied from rmap.c/etc.

Yes, the code needs to be cleanup up.

page_alloc.c renamed from page_alloc.c.orig +467 -1
@@ -33,6 +33,8 @@
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/nodemask.h>
+#include <linux/rmap.h>
+#include <linux/mm_inline.h>
 
 #include <asm/tlbflush.h>
 
@@ -97,7 +99,471 @@
 	page->mapping = NULL;
 }
 
-#ifndef CONFIG_HUGETLB_PAGE
+#define REMAP_FAIL 0
+#define REMAP_SUCCESS 1
+
+
+void page_remove_rmap(struct page *page);
+void page_add_anon_rmap(struct page *page,
+        struct vm_area_struct *vma, unsigned long address);
+struct anon_vma *page_lock_anon_vma(struct page *page);
+inline unsigned long avma_address(struct page *page, struct vm_area_struct *vma);
+
+inline unsigned long
+avma_address(struct page *page, struct vm_area_struct *vma)
+{
+        pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
+        unsigned long address;
+
+        address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
+
+        if (unlikely(address < vma->vm_start || address >= vma->vm_end)) {
+                /* page should be within any vma from prio_tree_next */
+		printk(KERN_ERR "address: %x pgoff:%x vma->start:%x vma->end:%x\n",
+				address, pgoff,vma->vm_start, vma->vm_end );
+                BUG_ON(!PageAnon(page));
+                return -EFAULT;
+        }
+        return address;
+}
+
+
+
+int try_to_remap_file(struct page *page, struct page *newpage, struct vm_area_struct *vma)
+{
+	unsigned long address;
+	struct mm_struct *mm = vma->vm_mm;	
+	pgd_t *pgd;
+        pmd_t *pmd;
+        pte_t *pte;
+        pte_t pteval;
+	int ret;
+
+	printk(KERN_ERR "try_to_remap_file!\n");
+
+	if (!mm->rss) 
+		return REMAP_FAIL;
+
+	address = avma_address(page, vma);
+
+
+	pgd = pgd_offset(mm, address);
+	if (!pgd_present(*pgd))
+		goto out_unlock;
+
+	pmd = pmd_offset(pgd, address);
+	if (!pmd_present(*pmd))
+		goto out_unlock;
+
+
+        pte = pte_offset_map(pmd, address);
+        if (!pte_present(*pte))
+                goto out_unlock;
+
+        if (page_to_pfn(page) != pte_pfn(*pte))
+		goto out_unlock;
+	
+	if ((vma->vm_flags & (VM_LOCKED|VM_RESERVED))) 
+		goto out_unlock;
+
+	/* Nuke the pte */
+
+        flush_cache_page(vma, address);
+
+       pteval = ptep_clear_flush(vma, address, pte);
+
+	page_remove_rmap(page);
+
+	/* transfer the dirty bit to the new page */
+	if (pte_dirty(pteval))
+		set_page_dirty(newpage);
+
+	pteval = mk_pte(newpage, vma->vm_page_prot);
+
+	set_pte(pte, pteval);
+
+	page_add_file_rmap(newpage);
+
+	return REMAP_SUCCESS;
+
+out_unlock:
+	return REMAP_FAIL;
+}
+
+
+
+
+int try_to_remap_anon(struct page *page, struct page *newpage, struct vm_area_struct *vma)
+{
+	unsigned long address;
+	struct mm_struct *mm = vma->vm_mm;	
+	pgd_t *pgd;
+        pmd_t *pmd;
+        pte_t *pte;
+        pte_t pteval;
+	int ret;
+
+
+	if (!vma)
+		printk(KERN_ERR "!vma\n");
+
+	spin_lock(&mm->page_table_lock);
+
+	address = avma_address(page, vma);
+
+	if (address == -EFAULT) 
+		return REMAP_FAIL;
+
+	if (!mm) 
+		return REMAP_FAIL;
+
+	if (!mm->rss) 
+		return REMAP_FAIL;
+
+	pgd = pgd_offset(mm, address);
+	if (!pgd_present(*pgd)) 
+		goto out_unlock;
+
+	pmd = pmd_offset(pgd, address);
+	if (!pmd_present(*pmd))
+		goto out_unlock;
+
+        pte = pte_offset_map(pmd, address);
+        if (!pte_present(*pte))
+                goto out_unlock;
+
+        if (page_to_pfn(page) != pte_pfn(*pte))
+		goto out_unlock;
+	
+	if ((vma->vm_flags & (VM_LOCKED|VM_RESERVED)))
+		ret = REMAP_FAIL;
+
+	/* Nuke the pte */
+
+        flush_cache_page(vma, address);
+        pteval = ptep_clear_flush(vma, address, pte);
+
+	page_remove_rmap(page);
+
+	/* transfer the dirty bit to the new page */
+	if (pte_dirty(pteval))
+		set_page_dirty(newpage);
+
+	pteval = mk_pte(newpage, vma->vm_page_prot);
+
+	set_pte(pte, pteval);
+
+	page_add_anon_rmap(newpage, vma, address);
+
+	spin_unlock(&mm->page_table_lock);
+
+	return REMAP_SUCCESS;
+
+out_unlock:
+	spin_unlock(&mm->page_table_lock);
+	return REMAP_FAIL;
+
+}
+
+/* Move LRU pages to other locations, undo the remapping operation 
+* if any of the mapped pte's fails to be remapped.
+* 
+*/
+
+int can_move_page(struct page *page) 
+{
+	int ret;
+	int ptes_unmapped = 0;
+	struct page *newpage;
+
+	if (PageLocked(page))
+		return 0;
+
+	if (PageReserved(page))
+		return 0;
+
+	if (PageWriteback(page))
+		return 0;
+
+	if (page_count(page) == 0)
+		return 1;
+
+	if (PageLRU(page)) {
+		if (PageAnon(page) && page_count(page) == 1 + PageSwapCache(page)) {
+			struct anon_vma *anon_vma;
+			struct vm_area_struct *vma;
+			unsigned long anon_mapping = (unsigned long) page->mapping;
+			unsigned long savedindex;
+			int error;
+			
+			newpage = alloc_pages(GFP_HIGHUSER, 0);
+
+			if (PageSwapCache(page) &&
+				page_count(page) != page_mapcount(page) + 1) {
+					free_page(newpage);
+					goto out;
+			}
+
+			if (!PageAnon(page) || anon_mapping != page->mapping) {
+				free_page (newpage);
+				goto out;
+			}
+
+			page_cache_get(page);
+
+			if (TestSetPageLocked(page)) {
+				free_page(newpage);
+				page_cache_release(page);
+				goto out;
+			}
+
+			if (PageSwapCache(page)) { 
+				write_lock_irq(&swapper_space.tree_lock);
+				/* recheck under swapper address space tree lock */
+				if (!PageSwapCache(page) || page_count(page) != 3) {
+					write_unlock_irq(&swapper_space.tree_lock);
+					free_page(newpage);
+					unlock_page(page);
+					page_cache_release(page);
+				}
+				radix_tree_delete(&swapper_space.page_tree, page->private);
+				savedindex = page->private;
+			}
+
+			anon_vma = page_lock_anon_vma(page);
+
+			if (!anon_vma)  {
+				free_page(newpage);
+				page_cache_release(page);
+				goto out;
+			}
+			
+			list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+				ret = try_to_remap_anon(page, newpage, vma);
+				if (ret == REMAP_FAIL) {
+					if (PageSwapCache(page))
+						write_unlock_irq(&swapper_space
+							.tree_lock);
+					spin_unlock(&anon_vma->lock);
+					free_page(newpage);
+					unlock_page(page);
+					page_cache_release(page);
+					goto redo_unmaps;
+				}
+				ptes_unmapped++;
+			}
+
+			copy_highpage(newpage, page);
+
+			unlock_page(page);
+
+			page_cache_release(page);
+			page_cache_release(page);
+
+			newpage->private = savedindex;
+	
+			if (PageSwapCache(page)) {
+				error = radix_tree_insert(&swapper_space.page_tree,
+                   						savedindex, newpage);
+
+				//if (error) 
+			}
+
+			
+			spin_unlock(&anon_vma->lock);
+			write_unlock_irq(&swapper_space.tree_lock);
+
+			return 1;
+
+		} else if (!PageAnon(page) && 
+				page_count(page) == 1) {
+			struct vm_area_struct *vma;
+			struct prio_tree_iter iter;
+			struct zone *zone = page_zone(page);
+			struct address_space *mapping = page->mapping;
+			struct page *testpage;
+			int mapped;
+			pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - 
+					PAGE_SHIFT);
+			pgoff_t savedindex = page->index;
+
+			if (!mapping)
+				goto out;
+
+			if (!list_empty(&mapping->i_mmap_nonlinear)) {
+				spin_unlock(&mapping->i_mmap_lock);
+				goto out;
+			}
+
+			if (PagePrivate(page))
+				printk(KERN_ERR "PagePrivate!\n");
+			if (PageWriteback(page)) {
+				printk(KERN_ERR "PageWriteback! quitting\n");
+				goto out;
+			}
+
+			newpage = alloc_pages(GFP_HIGHUSER, 0);
+
+			if (page_count(page) != 1 ||
+			  !PageLRU(page) || PageAnon(page) ||
+			  page->mapping != mapping ||
+			  page->index != savedindex) {
+				free_page(newpage);
+				goto out;
+			}
+
+			page_cache_get(page);
+
+			if (TestSetPageLocked(page)) {
+				page_cache_release(page);
+				printk(KERN_ERR "page locked!!!\n");
+				goto out;
+			}
+
+			// remove radix entry and block page faults for SMP systems
+
+		        spin_lock(&mapping->i_mmap_lock);
+
+			mapped = page_mapcount(page);
+
+			vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, 
+				pgoff, pgoff) 
+			{
+				ret = try_to_remap_file(page, newpage, vma);
+				if (ret == REMAP_FAIL) {
+					unlock_page(page);
+					goto redo_unmaps;
+				}
+				ptes_unmapped++;
+				mapped--;
+				if (!mapped)
+					break;
+					
+			}
+
+			if (TestClearPageLRU(page))
+				del_page_from_lru(zone, page);
+			
+			remove_from_page_cache(page);
+
+			copy_highpage(newpage, page);
+
+			newpage->flags = page->flags;
+
+			unlock_page(page);
+
+			add_to_page_cache_lru(newpage, mapping, savedindex, 
+				GFP_KERNEL);
+
+			page_cache_release(page);
+			page_cache_release(page);
+
+			unlock_page(newpage);
+
+		        spin_unlock(&mapping->i_mmap_lock);
+			return 1;
+		}
+
+	}
+
+
+out:
+	preempt_enable();
+	return 0;
+
+redo_unmaps:
+	free_page(newpage);
+	printk(KERN_ERR "unmap PTE failed!@#$^5! ptes_unmapped:%d\n", ptes_unmapped);
+	return 0;
+}
+
+#define MAX_ORDER_DEC	3	/* maximum order decrease */
+
+int coalesce_memory(unsigned int order, struct zone *zone)
+{
+	unsigned int torder;
+	unsigned int nr_freed_pages = 0, nr_pages = 0;
+	
+	if (order < 1) {
+		printk(KERN_ERR "order <= 2");
+		return -1;
+	}
+
+	preempt_disable();
+
+	for (torder = order - 1; torder > order - MAX_ORDER_DEC; torder--) {
+		struct list_head *entry;
+		struct page *pwalk, *page;
+		int walkcount = 0;
+		struct free_area *area = zone->free_area + torder;
+		nr_pages = (1UL << order) - (1UL << torder); 
+
+		entry = area->free_list.next;
+
+		while (entry != &area->free_list) {
+			int ret;
+			page = list_entry(entry, struct page, lru);
+			entry = entry->next;
+
+			pwalk = page;
+
+			/* Look backwards */
+
+			for (walkcount = 1; walkcount<nr_pages; walkcount++) {
+				pwalk = page-walkcount;
+
+				ret = can_move_page(pwalk);
+				if (ret)
+					nr_freed_pages++;
+				else
+					goto forward;
+
+				if (nr_freed_pages == nr_pages)
+					goto success;
+					
+			}
+
+forward:
+
+			pwalk = page;
+
+			/* Look forward, skipping the page frames from this 
+			  high order page we are looking at */
+
+			for (walkcount = (1UL << torder); walkcount<nr_pages; 
+					walkcount++) {
+				pwalk = page+walkcount;
+
+				ret = can_move_page(pwalk);
+
+				if (ret) 
+					nr_freed_pages++;
+				else
+					goto loopey;
+
+				if (nr_freed_pages == nr_pages)
+					goto success;
+			}
+
+loopey:
+	
+//			goto bailout;
+		}
+	}
+
+bailout:
+	preempt_enable();
+	printk(KERN_ERR "failure nr_pages:%d nr_freed_pages:%d!\n", nr_pages,
+nr_freed_pages);
+return 0;
+
+success:
+printk(KERN_ERR "SUCCESS coalesced %d pages!\n", nr_freed_pages);
+return 1;
+	
+}
+
+#ifndef CONFIG_HUGETL _PAGE
 #define prep_compound_page(page, order) do { } while (0)
 #define destroy_compound_page(page, order) do { } while (0)
 #else

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#1 lore raw
Quoted 10 lines · Marcelo Tosatti

The following patch implements a "coalesce_memory()" function which takes "zone" and "order" as a parameter.

It tries to move enough physically nearby pages to form a free area of "order" size.

It does that by checking whether the page can be moved, allocating a new page, unmapping the pte's to it, copying data to new page, remapping the ptes, and reinserting the page on the radix/LRU.

Presumably this duplicates some of the memory hot-remove patches.

Apparently Dave Hansen has working and sane-looking hot remove code which is in a close-to-submittable state.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#2 lore raw
Quoted 5 lines · Andrew Morton
Quoted 4 lines · Marcelo Tosatti

The following patch implements a "coalesce_memory()" function which takes "zone" and "order" as a parameter.

Quoted 8 lines
Quoted 6 lines

It tries to move enough physically nearby pages to form a free area of "order" size.

It does that by checking whether the page can be moved, allocating a new page, unmapping the pte's to it, copying data to new page, remapping the ptes, and reinserting the page on the radix/LRU.

Presumably this duplicates some of the memory hot-remove patches.

As far as I have researched, the memory moving/remapping code on the hot remove patches dont work correctly. Please correct me.

And what I've seen (from the Fujitsu guys) was quite ugly IMHO.

Quoted 2 lines

Apparently Dave Hansen has working and sane-looking hot remove code which is in a close-to-submittable state.

Dave?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#3 lore raw
Quoted 5 lines · Marcelo Tosatti

As far as I have researched, the memory moving/remapping code on the hot remove patches dont work correctly. Please correct me.

And what I've seen (from the Fujitsu guys) was quite ugly IMHO.

That's a totally different patch.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw
In reply to Marcelo Tosatti
Quoted 5 lines · Marcelo Tosatti
Quoted 1 line · Andrew Morton

Presumably this duplicates some of the memory hot-remove patches.

As far as I have researched, the memory moving/remapping code on the hot remove patches dont work correctly. Please correct me.

I definitely see some commonality, but Marcelo's approach has handling for the different kinds of pages broken out much more nicely. Can't tell yet if this produces extra code, or is just plain better.

We worked pretty hard to try and copy as little code as possible. Was there any reason that there was so much stuff copied out of rmap.c? Just for proof-of-concept?

Here's one of the recent patch sets that we're working on:

http://sprucegoose.sr71.net/patches/2.6.9-rc2-mm4-mhp-test2/

In that directory, the K* patches hijack some of the swap code (but require memory pressure to work last time I tried), and the p000* patches (by Hirokazu Takahashi) actively migrate pages around. Both approaches work, but the K* one is smaller and less intrusive, while the p000* one is much more complete. They may end up being able to coexist in the end.

Quoted 1 line

And what I've seen (from the Fujitsu guys) was quite ugly IMHO.

I don't work for Fujitsu :) Please take a look at the patches in the above directory and see what you think. I'm sure you have some very good stuff in your patch, but I need to take a closer look.

I'm just about to head out of town for the weekend, but I'll take a much more detailed look on Monday.

Signature · 8 lines
Dave Hansen
[email protected]

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#5 lore raw
Quoted 7 lines · Dave Hansen
Quoted 5 lines · Marcelo Tosatti
Quoted 1 line · Andrew Morton

Presumably this duplicates some of the memory hot-remove patches.

As far as I have researched, the memory moving/remapping code on the hot remove patches dont work correctly. Please correct me.

Quoted 7 lines

I definitely see some commonality, but Marcelo's approach has handling for the different kinds of pages broken out much more nicely. Can't tell yet if this produces extra code, or is just plain better.

We worked pretty hard to try and copy as little code as possible. Was there any reason that there was so much stuff copied out of rmap.c? Just for proof-of-concept?

Just proof of concept really, to have an equivalent of "try_to_unmap()" - which you call from the migrate page code.

Just that "try_to_remap_{file,anon}" do the pte clearing + remapping in one function.

Quoted 10 lines

Here's one of the recent patch sets that we're working on:

http://sprucegoose.sr71.net/patches/2.6.9-rc2-mm4-mhp-test2/

In that directory, the K* patches hijack some of the swap code (but require memory pressure to work last time I tried), and the p000* patches (by Hirokazu Takahashi) actively migrate pages around. Both approaches work, but the K* one is smaller and less intrusive, while the p000* one is much more complete. They may end up being able to coexist in the end.

The page migration code (p000*) looks nice - quite complete indeed (nice error handling, etc) but somewhat specific to the migration procedure, which is more critical (cannot fail so easily as) then the remapping for high-order allocations.

For example this in migrate_page_common

+ switch (ret) {
+ case 0:
+ case -ENOENT:
+ copy_highpage(newpage, page);
+ return ret;
+ case -EBUSY:
+ return ret;
+ case -EAGAIN:
+ writeback_and_free_buffers(page);
+ unlock_page(page);
+ msleep(10);
+ timeout -= 10;
+ lock_page(page);
+ continue;

Which retries undefinately to migrate the page

For the "defragmentation" operation we want to do an "easy" try - ie if we can't remap giveup.

I feel we should try to "untie" the code which checks for remapping availability / does the remapping from the page migration - so to be able to share the most code between it and other users of the same functionality.

Curiosity: How did you guys test the migration operation? Several threads on several processors operating on the memory, etc?

Quoted 6 lines

I don't work for Fujitsu :) Please take a look at the patches in the above directory and see what you think. I'm sure you have some very good stuff in your patch, but I need to take a closer look.

I'm just about to head out of town for the weekend, but I'll take a much more detailed look on Monday.

Cool. I'll take a closer look at the relevant parts of memory hotplug patches this weekend, hopefully. See if I can help with testing of these patches too.

Andrew, what are your thoughts wrt merging this to mainline?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw
Quoted 7 lines · Marcelo Tosatti
Quoted 5 lines

Here's one of the recent patch sets that we're working on:

http://sprucegoose.sr71.net/patches/2.6.9-rc2-mm4-mhp-test2/

...
Andrew, what are your thoughts wrt merging this to mainline?

It's the first I've seen of it. I guess I'd be looking for testing results as well as the outcome of discussions/review with the ia64 guys whose hardware is not quite as cooperative as that on the ppc64 machines. --
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#7 lore raw
In reply to Marcelo Tosatti

Hello, Marcelo.

Generic memory defragmentation will be very nice for me to implement hugetlbpage migration, as allocating a new hugetlbpage is a hard job.

Quoted 6 lines

For the "defragmentation" operation we want to do an "easy" try - ie if we can't remap giveup.

I feel we should try to "untie" the code which checks for remapping availability / does the remapping from the page migration - so to be able to share the most code between it and other users of the same functionality.

I think it's possible to introduce non-wait mode to the migration code, as you may expect. Shall I implement it?

Quoted 2 lines

Curiosity: How did you guys test the migration operation? Several threads on several processors operating on the memory, etc?

I always test it with the zone hotplug emulation patch, which Mr.Iwamoto has made. I usually run following jobs concurrently while zones are added and removed repeatedly on a SMP machine.
- making linux kernel
- copying file trees.
- overwriting file trees.
- removing file trees
- some pages are swapped out automatically:)

And Mr.Iwamoto has some small programs to check any kind of page can be migrated. The programs repeat one of following actions:
- read/write files .
- use MAP_SHARED and MAP_PRIVATE mmap()'s and read/write there.
- use Direct I/O.
- use AIO.
- fork to have COW pages.
- use shmem.
- use sendfile.

Quoted 2 lines

Cool. I'll take a closer look at the relevant parts of memory hotplug patches this weekend, hopefully. See if I can help with testing of these patches too.

Any comments are very welcome.

Thank you,
Hirokazu Takahashi.

#8 lore raw
Quoted 37 lines · Hirokazu Takahashi

Hello, Marcelo.

Generic memory defragmentation will be very nice for me to implement hugetlbpage migration, as allocating a new hugetlbpage is a hard job.

Quoted 6 lines

For the "defragmentation" operation we want to do an "easy" try - ie if we can't remap giveup.

I feel we should try to "untie" the code which checks for remapping availability / does the remapping from the page migration - so to be able to share the most code between it and other users of the same functionality.

I think it's possible to introduce non-wait mode to the migration code, as you may expect. Shall I implement it?

Quoted 2 lines

Curiosity: How did you guys test the migration operation? Several threads on several processors operating on the memory, etc?

I always test it with the zone hotplug emulation patch, which Mr.Iwamoto has made. I usually run following jobs concurrently while zones are added and removed repeatedly on a SMP machine.
- making linux kernel
- copying file trees.
- overwriting file trees.
- removing file trees
- some pages are swapped out automatically:)

And Mr.Iwamoto has some small programs to check any kind of page can be migrated. The programs repeat one of following actions:
- read/write files .
- use MAP_SHARED and MAP_PRIVATE mmap()'s and read/write there.
- use Direct I/O.
- use AIO.
- fork to have COW pages.
- use shmem.
- use sendfile.

Quoted 4 lines
Quoted 2 lines

Cool. I'll take a closer look at the relevant parts of memory hotplug patches this weekend, hopefully. See if I can help with testing of these patches too.

Any comments are very welcome.

I have a few comments about the code:

1)
I'm pretty sure you should transfer the radix tree tag at radix_tree_replace(). If for example you transfer a dirty tagged page to another zone, an mpage_writepages() will miss it (because it uses pagevec_lookup_tag(PAGECACHE_DIRTY_TAG)).

Should be quite trivial to do (save tags before deleting and set to new entry, all in radix_tree_replace).

My implementation also contained the same bug.

2)
At migrate_onepage you add anonymous pages which aren't swap allocated to the swap cache
+ /*
+ * Put the page in a radix tree if it isn't in the tree yet.
+ */
+#ifdef CONFIG_SWAP
+ if (PageAnon(page) && !PageSwapCache(page))
+ if (!add_to_swap(page, GFP_KERNEL)) {
+ unlock_page(page);
+ return ERR_PTR(-ENOSPC);
+ }
+#endif /* CONFIG_SWAP */

Why's that? You can copy anonymous pages without adding them to swap (thats what the patch I posted does).

3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Is that assumption always valid?

In theory there is no need to writeout pages when migrating them to other zones - they will be copied and the dirty information retained (either in the PageDirty bit or radix tree tag).

I just noticed you do that on further patches (migrate_page_buffer), but AFAICS the writeout remains. Why arent you using migrate_page_buffer yet?

I think the final aim should be to remove the need for "pageout()" completly.

4)
About implementing a nonblocking version of it. The easier way, it seems to me, is to pass a "block" argument to generic_migrate_page() and use that.

Questions: are there any documents on the memory hotplug userspace tools? Where can I find them?

Are Iwamoto's test programs available?

In general the code looks nice to me! I'll jump in and help with testing.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#9 lore raw

Hi,

Quoted 13 lines
Quoted 4 lines
Quoted 2 lines

Cool. I'll take a closer look at the relevant parts of memory hotplug patches this weekend, hopefully. See if I can help with testing of these patches too.

Any comments are very welcome.

I have a few comments about the code:

1)
I'm pretty sure you should transfer the radix tree tag at radix_tree_replace(). If for example you transfer a dirty tagged page to another zone, an mpage_writepages() will miss it (because it uses pagevec_lookup_tag(PAGECACHE_DIRTY_TAG)).

Quoted 4 lines

Should be quite trivial to do (save tags before deleting and set to new entry, all in radix_tree_replace).

My implementation also contained the same bug.

Yes, it's one of the issues to do. The tag should be transferred in radix_tree_replace() as you pointed out. The current implementation sets the tag in set_page_dirty(newpage).

Quoted 14 lines

2)
At migrate_onepage you add anonymous pages which aren't swap allocated to the swap cache

(quoted diff) +10 -0
+       /*
+        * Put the page in a radix tree if it isn't in the tree yet.
+        */
+#ifdef CONFIG_SWAP
+       if (PageAnon(page) && !PageSwapCache(page))
+               if (!add_to_swap(page, GFP_KERNEL)) {
+                       unlock_page(page);
+                       return ERR_PTR(-ENOSPC);
+               }
+#endif /* CONFIG_SWAP */
Quoted 2 lines

Why's that? You can copy anonymous pages without adding them to swap (thats what the patch I posted does).

The reason is to guarantee that any anonymous page can be migrated anytime. I want to block newly occurred accesses to the page during the migration because it can't be migrated if there remain some references on it by system calls, direct I/O and page faults.

Your approach will work fine on most of anonymous pages, which aren't heavily accessed. I think it will be enough for memory defragmentation.

Quoted 5 lines

3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Is that assumption always valid?

-EAGAIN means that the page may require to be written back or just to wait for a while since the page is just referred by system call or pagefault handler.

Quoted 6 lines

In theory there is no need to writeout pages when migrating them to other zones - they will be copied and the dirty information retained (either in the PageDirty bit or radix tree tag).

I just noticed you do that on further patches (migrate_page_buffer), but AFAICS the writeout remains. Why arent you using migrate_page_buffer yet?

I've designed migrate_page_buffer() for this purpose. At this moment ext2 only uses this yet.

Quoted 2 lines

I think the final aim should be to remove the need for "pageout()" completly.

Yes!

Quoted 4 lines

4)
About implementing a nonblocking version of it. The easier way, it seems to me, is to pass a "block" argument to generic_migrate_page() and use that.

Yes.

Quoted 2 lines

Questions: are there any documents on the memory hotplug userspace tools? Where can I find them?

IBM guys and Fujitsu guys are designing user interface independently. IBM team is implementing memory section hotplug while Fujitsu team try to implement NUMA node hotplug. But both of the designs use regular hot-plug mechanism, which kicks /sbin/hotplug script to control devices via sysfs.

Dave, would you explain about it?

Quoted 1 line

Are Iwamoto's test programs available?

Ok, I'll notice him to post them.

Quoted 2 lines

In general the code looks nice to me! I'll jump in and help with testing.

I appreciate your offer. I'm very happy with that.

Thank you,
Hirokazu Takahasih.

#10 lore raw
Quoted 15 lines · Hirokazu Takahashi

Hi,

Quoted 13 lines
Quoted 4 lines
Quoted 2 lines

Cool. I'll take a closer look at the relevant parts of memory hotplug patches this weekend, hopefully. See if I can help with testing of these patches too.

Any comments are very welcome.

I have a few comments about the code:

1)
I'm pretty sure you should transfer the radix tree tag at radix_tree_replace(). If for example you transfer a dirty tagged page to another zone, an mpage_writepages() will miss it (because it uses pagevec_lookup_tag(PAGECACHE_DIRTY_TAG)).

Quoted 8 lines
Quoted 4 lines

Should be quite trivial to do (save tags before deleting and set to new entry, all in radix_tree_replace).

My implementation also contained the same bug.

Yes, it's one of the issues to do. The tag should be transferred in radix_tree_replace() as you pointed out. The current implementation sets the tag in set_page_dirty(newpage).

Oh I missed that, right.

But yes, anyway, the tag should be transferred at radix_tree_replace (earlier) or pagevec_lookup_tag() can miss those pages.

Quoted 14 lines
Quoted 14 lines

2)
At migrate_onepage you add anonymous pages which aren't swap allocated to the swap cache

(quoted diff) +10 -0
+       /*
+        * Put the page in a radix tree if it isn't in the tree yet.
+        */
+#ifdef CONFIG_SWAP
+       if (PageAnon(page) && !PageSwapCache(page))
+               if (!add_to_swap(page, GFP_KERNEL)) {
+                       unlock_page(page);
+                       return ERR_PTR(-ENOSPC);
+               }
+#endif /* CONFIG_SWAP */
Quoted 7 lines
Quoted 2 lines

Why's that? You can copy anonymous pages without adding them to swap (thats what the patch I posted does).

The reason is to guarantee that any anonymous page can be migrated anytime. I want to block newly occurred accesses to the page during the migration because it can't be migrated if there remain some references on it by system calls, direct I/O and page faults.

It would be nice if we could block pte faults in a way such to not need adding each anonymous page to swap. It can be too costly if you have a lot memory and it makes the whole operation dependable on swap size (if you dont have enough swap, you're dead).

Maybe hold mm->page_table_lock (might be too costly in terms of CPU time, but since migration is not a common operation anyway), or create a semaphore?

Quoted 2 lines

Your approach will work fine on most of anonymous pages, which aren't heavily accessed. I think it will be enough for memory defragmentation.

Yes...

Quoted 7 lines
Quoted 5 lines

3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Is that assumption always valid?

-EAGAIN means that the page may require to be written back

But why is it needed to writeout pages? We shouldnt need to. At least from what I can understand.

Quoted 3 lines

or
just to wait for a while since the page is just referred by system call or pagefault handler.

I'm not sure if making that assumption is always valid.

Kernel code can have an additional count on the page meaning "this page is pinned, dont move it". At least that should be valid.

Any piece of code which holds a reference on a page for a long time is going to be a pain for the algorithm right?

Quoted 9 lines
Quoted 6 lines

In theory there is no need to writeout pages when migrating them to other zones - they will be copied and the dirty information retained (either in the PageDirty bit or radix tree tag).

I just noticed you do that on further patches (migrate_page_buffer), but AFAICS the writeout remains. Why arent you using migrate_page_buffer yet?

I've designed migrate_page_buffer() for this purpose. At this moment ext2 only uses this yet.

Ah ok I haven't looked at those patches.

Quoted 11 lines
Quoted 2 lines

I think the final aim should be to remove the need for "pageout()" completly.

Yes!

Quoted 4 lines

4)
About implementing a nonblocking version of it. The easier way, it seems to me, is to pass a "block" argument to generic_migrate_page() and use that.

Yes.

OK. I'll try to implement it this week (plus the radix_tree_replace tag thingie).

Quoted 10 lines
Quoted 2 lines

Questions: are there any documents on the memory hotplug userspace tools? Where can I find them?

IBM guys and Fujitsu guys are designing user interface independently. IBM team is implementing memory section hotplug while Fujitsu team try to implement NUMA node hotplug. But both of the designs use regular hot-plug mechanism, which kicks /sbin/hotplug script to control devices via sysfs.

Dave, would you explain about it?

Please :)

Quoted 8 lines
Quoted 1 line

Are Iwamoto's test programs available?

Ok, I'll notice him to post them.

Quoted 2 lines

In general the code looks nice to me! I'll jump in and help with testing.

I appreciate your offer. I'm very happy with that.

Me too! :)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw

Hi, Marcelo

Quoted 22 lines
Quoted 14 lines
Quoted 14 lines

2)
At migrate_onepage you add anonymous pages which aren't swap allocated to the swap cache

(quoted diff) +10 -0
+       /*
+        * Put the page in a radix tree if it isn't in the tree yet.
+        */
+#ifdef CONFIG_SWAP
+       if (PageAnon(page) && !PageSwapCache(page))
+               if (!add_to_swap(page, GFP_KERNEL)) {
+                       unlock_page(page);
+                       return ERR_PTR(-ENOSPC);
+               }
+#endif /* CONFIG_SWAP */
Quoted 7 lines
Quoted 2 lines

Why's that? You can copy anonymous pages without adding them to swap (thats what the patch I posted does).

The reason is to guarantee that any anonymous page can be migrated anytime. I want to block newly occurred accesses to the page during the migration because it can't be migrated if there remain some references on it by system calls, direct I/O and page faults.

Quoted 7 lines

It would be nice if we could block pte faults in a way such to not need adding each anonymous page to swap. It can be too costly if you have a lot memory and it makes the whole operation dependable on swap size (if you dont have enough swap, you're dead).

Maybe hold mm->page_table_lock (might be too costly in terms of CPU time, but since migration is not a common operation anyway), or create a semaphore?

I think the problem of the holding mm->page_table_lock approach is that it doesn't allow the migration code blocked. the semaphore approach would be better.

I have another idea that each anonymous page can detach its swap entry after its migration. It can be done by remove_exclusive_swap_page() if the page is remapped to the same spaces forcibly by touch_unmapped_address() I made.

Quoted 9 lines
Quoted 2 lines

Your approach will work fine on most of anonymous pages, which aren't heavily accessed. I think it will be enough for memory defragmentation.

Yes...

Quoted 4 lines
Quoted 4 lines

3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Is that assumption always valid?

-EAGAIN means that the page may require to be written back

But why is it needed to writeout pages? We shouldnt need to. At least from what I can understand.

The migration code allows each filesystem to implement its own migration code or just use migrate_page_buffer() or migrate_page_common().

migrate_page_common() is a default function if filesystem doesn't implement anything. The function is the most generic and it tries to writeback pages only if they are dirty and have buffers.

Quoted 8 lines
Quoted 3 lines

or
just to wait for a while since the page is just referred by system call or pagefault handler.

I'm not sure if making that assumption is always valid.

Kernel code can have an additional count on the page meaning "this page is pinned, dont move it". At least that should be valid.

Yes, I know. I have checked all of the code.

AIO event buffers are pinned, therefore the memory-hotplug team plans to make pages for the event buffers assigned to non-hotpluggable memory regions.

And pages in sendfile() might be pinned for a while in case of network problems. I think there may be some workarounds. The easiest way is just waiting its timeout, and another way is changing the mode of sendfile() to copy pages in advance.

Pages for NFS also might be pinned with network problems. One of the ideas is to restrict NFS to allocate pages from specific memory region, sot that all memory except the region can be hot-removed. And it's possible to implementing whole migrate_page method, which may handled stuck pages.

If the migration code is used for memory defragmentation, pinned pages must be avoided. I think it can be done with the non-blocking mode.

Quoted 9 lines

Any piece of code which holds a reference on a page for a long time is going to be a pain for the algorithm right?

Quoted 5 lines
Quoted 4 lines

4)
About implementing a nonblocking version of it. The easier way, it seems to me, is to pass a "block" argument to generic_migrate_page() and use that.

Quoted 4 lines
Quoted 1 line

Yes.

OK. I'll try to implement it this week (plus the radix_tree_replace tag thingie).

Thank you for that.

Hirokazu Takahashi.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#12 lore raw

Pa su , 03/10/2004 klokka 20:35, skreiv Hirokazu Takahashi:

Quoted 5 lines

Pages for NFS also might be pinned with network problems. One of the ideas is to restrict NFS to allocate pages from specific memory region, sot that all memory except the region can be hot-removed. And it's possible to implementing whole migrate_page method, which may handled stuck pages.

Why do you want to special-case this?

The above is a generic condition: any filesystem can suffer from the equivalent problem of a failure or slow response in the underlying device. Making an NFS-specific hack is just counter-productive to solving the generic problem.

Cheers,
Trond

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#13 lore raw

Hello,

Quoted 6 lines
Quoted 5 lines

Pages for NFS also might be pinned with network problems. One of the ideas is to restrict NFS to allocate pages from specific memory region, sot that all memory except the region can be hot-removed. And it's possible to implementing whole migrate_page method, which may handled stuck pages.

Quoted 6 lines

Why do you want to special-case this?

The above is a generic condition: any filesystem can suffer from the equivalent problem of a failure or slow response in the underlying device. Making an NFS-specific hack is just counter-productive to solving the generic problem.

However, while network is down network/cluster filesystems might not release pages forever unlike in the case of block devices, which may timeout or returns a error in case of failure.

Each filesystem can control what the migration code does. If it doesn't have anything to help memory migration, it's possible to wait for the network coming up before starting memory migration, or give up it if the network happen to be down. That's no problem.

Thank you,
Hirokazu Takahashi.

#14 lore raw

Pa su , 03/10/2004 klokka 22:03, skreiv Hirokazu Takahashi:

Quoted 3 lines

However, while network is down network/cluster filesystems might not release pages forever unlike in the case of block devices, which may timeout or returns a error in case of failure.

Where is the difference? As far as the VM is concerned, it is a latency problem. The fact of whether or not it is a permanent hang, a hang with a long timeout, or just a slow device is irrelevant because the VM doesn't actually know about these devices.

Quoted 4 lines

Each filesystem can control what the migration code does. If it doesn't have anything to help memory migration, it's possible to wait for the network coming up before starting memory migration, or give up it if the network happen to be down. That's no problem.

Wrong. It *is* a problem: Filesystems aren't required to know anything about the particulars of the underlying block/network/... device timeout semantics either.

Think, for instance about EXT2. Where in the current code do you see that it is required to detect that it is running on top of something like the NBD device? Where does it figure out what the latencies of this device is?

AFAICS, most filesystems in linux/fs/* have no knowledge whatsoever about the underlying block/network/... devices and their timeout values. Basing your decision about whether or not you need to manage high latency situations just by inspecting the filesystem type is therefore not going to give very reliable results.

Cheers,
Trond

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#15 lore raw

Hello,

Yes, I know what you're talking about.
The current kernel doesn't have any features about it.

So that I've been wondering if there might be any good solution to help memory hot-removal. It would be nice if there were support from filesystems and block devices.

Quoted 23 lines
Quoted 3 lines

However, while network is down network/cluster filesystems might not release pages forever unlike in the case of block devices, which may timeout or returns a error in case of failure.

Where is the difference? As far as the VM is concerned, it is a latency problem. The fact of whether or not it is a permanent hang, a hang with a long timeout, or just a slow device is irrelevant because the VM doesn't actually know about these devices.

Quoted 4 lines

Each filesystem can control what the migration code does. If it doesn't have anything to help memory migration, it's possible to wait for the network coming up before starting memory migration, or give up it if the network happen to be down. That's no problem.

Wrong. It *is* a problem: Filesystems aren't required to know anything about the particulars of the underlying block/network/... device timeout semantics either.

Think, for instance about EXT2. Where in the current code do you see that it is required to detect that it is running on top of something like the NBD device? Where does it figure out what the latencies of this device is?

Quoted 5 lines

AFAICS, most filesystems in linux/fs/* have no knowledge whatsoever about the underlying block/network/... devices and their timeout values. Basing your decision about whether or not you need to manage high latency situations just by inspecting the filesystem type is therefore not going to give very reliable results.

Thank you,
Hirokazu Takahashi.

#16 lore raw
In reply to Hirokazu Takahashi
Quoted 29 lines · Hirokazu Takahashi

Hi, Marcelo

Quoted 27 lines
Quoted 14 lines
Quoted 14 lines

2)
At migrate_onepage you add anonymous pages which aren't swap allocated to the swap cache

(quoted diff) +10 -0
+       /*
+        * Put the page in a radix tree if it isn't in the tree yet.
+        */
+#ifdef CONFIG_SWAP
+       if (PageAnon(page) && !PageSwapCache(page))
+               if (!add_to_swap(page, GFP_KERNEL)) {
+                       unlock_page(page);
+                       return ERR_PTR(-ENOSPC);
+               }
+#endif /* CONFIG_SWAP */
Quoted 7 lines
Quoted 2 lines

Why's that? You can copy anonymous pages without adding them to swap (thats what the patch I posted does).

The reason is to guarantee that any anonymous page can be migrated anytime. I want to block newly occurred accesses to the page during the migration because it can't be migrated if there remain some references on it by system calls, direct I/O and page faults.

It would be nice if we could block pte faults in a way such to not need adding each anonymous page to swap. It can be too costly if you have a lot memory and it makes the whole operation dependable on swap size (if you dont have enough swap, you're dead).

Quoted 6 lines
Quoted 2 lines

Maybe hold mm->page_table_lock (might be too costly in terms of CPU time, but since migration is not a common operation anyway), or create a semaphore?

I think the problem of the holding mm->page_table_lock approach is that it doesn't allow the migration code blocked. the semaphore approach would be better.

OK, I think the problem is that can be more than one thread with different address spaces (ie different "current->mm", after fork) accessing the page.

Adding a waitqueue to "anon_vma" structure (to be slept at do_swap_page time, and awake after copy-page-and-flags/unlock), can be do the job I think.

Quoted 2 lines

I have another idea that each anonymous page can detach its swap entry after its migration.

Yes thats a nice idea.

Quoted 3 lines

It can be done by remove_exclusive_swap_page() if the page is remapped to the same spaces forcibly by touch_unmapped_address() I made.

touch_unmapped_address() ?

Quoted 16 lines
Quoted 9 lines
Quoted 2 lines

Your approach will work fine on most of anonymous pages, which aren't heavily accessed. I think it will be enough for memory defragmentation.

Yes...

Quoted 4 lines
Quoted 4 lines

3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Is that assumption always valid?

-EAGAIN means that the page may require to be written back

But why is it needed to writeout pages? We shouldnt need to. At least from what I can understand.

Quoted 7 lines

The migration code allows each filesystem to implement its own migration code or just use migrate_page_buffer() or migrate_page_common().

migrate_page_common() is a default function if filesystem doesn't implement anything. The function is the most generic and it tries to writeback pages only if they are dirty and have buffers.

The thing is: What is the point of writing out pages?

We're just trying to migrate pages to another zone.

If its under writeout, wait, if its dirty, just move it to the other zone.

Can you enlight me?

Quoted 20 lines
Quoted 8 lines
Quoted 3 lines

or
just to wait for a while since the page is just referred by system call or pagefault handler.

I'm not sure if making that assumption is always valid.

Kernel code can have an additional count on the page meaning "this page is pinned, dont move it". At least that should be valid.

Yes, I know. I have checked all of the code.

AIO event buffers are pinned, therefore the memory-hotplug team plans to make pages for the event buffers assigned to non-hotpluggable memory regions.

And pages in sendfile() might be pinned for a while in case of network problems. I think there may be some workarounds. The easiest way is just waiting its timeout, and another way is changing the mode of sendfile() to copy pages in advance.

Quoted 8 lines

Pages for NFS also might be pinned with network problems. One of the ideas is to restrict NFS to allocate pages from specific memory region, sot that all memory except the region can be hot-removed. And it's possible to implementing whole migrate_page method, which may handled stuck pages.

If the migration code is used for memory defragmentation, pinned pages must be avoided. I think it can be done with the non-blocking mode.

Right.

Quoted 9 lines
Quoted 9 lines

Any piece of code which holds a reference on a page for a long time is going to be a pain for the algorithm right?

Quoted 5 lines
Quoted 4 lines

4)
About implementing a nonblocking version of it. The easier way, it seems to me, is to pass a "block" argument to generic_migrate_page() and use that.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Yes.

OK. I'll try to implement it this week (plus the radix_tree_replace tag thingie).

Thank you for that.

Any news about Iwamoto's test programs? :)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#17 lore raw

Hi, Marcelo

Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
Quoted 7 lines
Quoted 2 lines

Why's that? You can copy anonymous pages without adding them to swap (thats what the patch I posted does).

The reason is to guarantee that any anonymous page can be migrated anytime. I want to block newly occurred accesses to the page during the migration because it can't be migrated if there remain some references on it by system calls, direct I/O and page faults.

It would be nice if we could block pte faults in a way such to not need adding each anonymous page to swap. It can be too costly if you have a lot memory and it makes the whole operation dependable on swap size (if you dont have enough swap, you're dead).

Maybe hold mm->page_table_lock (might be too costly in terms of CPU time, but since migration is not a common operation anyway), or create a semaphore?

Quoted 6 lines
Quoted 3 lines

I think the problem of the holding mm->page_table_lock approach is that it doesn't allow the migration code blocked. the semaphore approach would be better.

OK, I think the problem is that can be more than one thread with different address spaces (ie different "current->mm", after fork) accessing the page.

Yes.

Quoted 7 lines

Adding a waitqueue to "anon_vma" structure (to be slept at do_swap_page time, and awake after copy-page-and-flags/unlock), can be do the job I think.

Quoted 2 lines

I have another idea that each anonymous page can detach its swap entry after its migration.

Yes thats a nice idea.

I'll do it in a week.

Quoted 5 lines
Quoted 3 lines

It can be done by remove_exclusive_swap_page() if the page is remapped to the same spaces forcibly by touch_unmapped_address() I made.

touch_unmapped_address() ?

I've made two functions.

record_unmapped_address() holds mm and address where a target page has been mapped. touch_unmapped_address() remaps the page again.

Quoted 20 lines
Quoted 20 lines
Quoted 9 lines
Quoted 2 lines

Your approach will work fine on most of anonymous pages, which aren't heavily accessed. I think it will be enough for memory defragmentation.

Yes...

Quoted 4 lines
Quoted 4 lines

3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Is that assumption always valid?

-EAGAIN means that the page may require to be written back

But why is it needed to writeout pages? We shouldnt need to. At least from what I can understand.

The migration code allows each filesystem to implement its own migration code or just use migrate_page_buffer() or migrate_page_common().

Quoted 5 lines
Quoted 3 lines

migrate_page_common() is a default function if filesystem doesn't implement anything. The function is the most generic and it tries to writeback pages only if they are dirty and have buffers.

The thing is: What is the point of writing out pages?

It was the easiest way to handle pages with buffers when Iwamoto and I started to implement it. We thought it was slow but it would work for all kinds of filesystems.

Quoted 6 lines

We're just trying to migrate pages to another zone.

If its under writeout, wait, if its dirty, just move it to the other zone.

Can you enlight me?

Yes, I also realize that.
migrate_page_buffer() will do this, but I'm not certain it will work for all kinds of filesystems. I guess there might be some exceptions. We may need a special operation to handle pages on a filesystem, which has releasepage method.

Thanks,
Hirokazu Takahashi.

#18 lore raw
Quoted 9 lines · Hirokazu Takahashi

It was the easiest way to handle pages with buffers when Iwamoto and I started to implement it. We thought it was slow but it would work for all kinds of filesystems.

Quoted 5 lines

We're just trying to migrate pages to another zone.

If its under writeout, wait, if its dirty, just move it to the other zone.

Quoted 7 lines
Quoted 1 line

Can you enlight me?

Yes, I also realize that.
migrate_page_buffer() will do this, but I'm not certain it will work for all kinds of filesystems. I guess there might be some exceptions. We may need a special operation to handle pages on a filesystem, which has releasepage method.

It seems there is typo in the current version of the patch:

int try_to_migrate_pages(struct list_head *page_list) {
...

        current->flags |= PF_KSWAPD;    /*  It's fake */
        list_for_each_entry_safe(page, page2, page_list, lru) {
                /*
                 * Start writeback I/O if it's a dirty page with buffers
                 * and it doesn't have migrate_page method.
                 */
                if (PageDirty(page) && PagePrivate(page)) {
                        if (!TestSetPageLocked(page)) {
                                mapping = page_mapping(page);
                                if (!mapping || mapping->a_ops->migrate_page ||
						^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                    pageout(page, mapping) != PAGE_SUCCESS) {
                                        unlock_page(page);
                                }
                        }
                }

Shouldnt that be "!mapping->a_ops->migrate_page"?

That is, if we can't migrate the page, try to write it out?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#19 lore raw

Hi, Marcelo.

Quoted 5 lines

It seems there is typo in the current version of the patch:

int try_to_migrate_pages(struct list_head *page_list) {
...

Quoted 10 lines
        current->flags |= PF_KSWAPD;    /*  It's fake */
        list_for_each_entry_safe(page, page2, page_list, lru) {
                /*
                 * Start writeback I/O if it's a dirty page with buffers
                 * and it doesn't have migrate_page method.
                 */
                if (PageDirty(page) && PagePrivate(page)) {
                        if (!TestSetPageLocked(page)) {
                                mapping = page_mapping(page);
                                if (!mapping || mapping->a_ops->migrate_page ||

a> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Quoted 8 lines
                                    pageout(page, mapping) != PAGE_SUCCESS) {
                                        unlock_page(page);
                                }
                        }
                }

Shouldnt that be "!mapping->a_ops->migrate_page"?

"mapping->a_ops->migrate_page" is correct.

This code is just for optimization. If mapping->a_ops->migrate_page isn't implemented, migrate_page_common() is used to migrate the page. migrate_page_common() will try to write it back if it's dirty, so that it would be better to start writeback I/O for target pages in advance without waiting the I/O completions.

As you may know the migration code will work fine without this code.

Quoted 1 line

That is, if we can't migrate the page, try to write it out?

Thank you,
Hirokazu Takahashi.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#20 lore raw
Quoted 27 lines · Hirokazu Takahashi

Hi, Marcelo.

Quoted 5 lines

It seems there is typo in the current version of the patch:

int try_to_migrate_pages(struct list_head *page_list) {
...

Quoted 10 lines
        current->flags |= PF_KSWAPD;    /*  It's fake */
        list_for_each_entry_safe(page, page2, page_list, lru) {
                /*
                 * Start writeback I/O if it's a dirty page with buffers
                 * and it doesn't have migrate_page method.
                 */
                if (PageDirty(page) && PagePrivate(page)) {
                        if (!TestSetPageLocked(page)) {
                                mapping = page_mapping(page);
                                if (!mapping || mapping->a_ops->migrate_page ||

a> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Quoted 8 lines
                                    pageout(page, mapping) != PAGE_SUCCESS) {
                                        unlock_page(page);
                                }
                        }
                }

Shouldnt that be "!mapping->a_ops->migrate_page"?

Quoted 7 lines

"mapping->a_ops->migrate_page" is correct.

This code is just for optimization. If mapping->a_ops->migrate_page isn't implemented, migrate_page_common() is used to migrate the page. migrate_page_common() will try to write it back if it's dirty, so that it would be better to start writeback I/O for target pages in advance without waiting the I/O completions.

Right. But if migrate_page _is_ implemented, we also start writeback!

Shouldnt that be "if we dont have migrate_page(), start writeback, since in this case well use migrate_page_common() anyway."

It seems the logic is inverted, or maybe I'm wrong.

Quoted 1 line

As you may know the migration code will work fine without this code.

OK!

Quoted 1 line
Quoted 1 line

That is, if we can't migrate the page, try to write it out?

I just didnt understand the logic very well, maybe I should just go reread the code.

Thanks!
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#21 lore raw

Hi, Marcelo.

Quoted 36 lines · Hirokazu Takahashi
Quoted 27 lines

Hi, Marcelo.

Quoted 23 lines

It seems there is typo in the current version of the patch:

int try_to_migrate_pages(struct list_head *page_list) {
...

        current->flags |= PF_KSWAPD;    /*  It's fake */
        list_for_each_entry_safe(page, page2, page_list, lru) {
                /*
                 * Start writeback I/O if it's a dirty page with buffers
                 * and it doesn't have migrate_page method.
                 */
                if (PageDirty(page) && PagePrivate(page)) {
                        if (!TestSetPageLocked(page)) {
                                mapping = page_mapping(page);
                                if (!mapping || mapping->a_ops->migrate_page ||
						^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                    pageout(page, mapping) != PAGE_SUCCESS) {
                                        unlock_page(page);
                                }
                        }
                }
Quoted 1 line

Shouldnt that be "!mapping->a_ops->migrate_page"?

Quoted 7 lines

"mapping->a_ops->migrate_page" is correct.

This code is just for optimization. If mapping->a_ops->migrate_page isn't implemented, migrate_page_common() is used to migrate the page. migrate_page_common() will try to write it back if it's dirty, so that it would be better to start writeback I/O for target pages in advance without waiting the I/O completions.

Quoted 6 lines

Right. But if migrate_page _is_ implemented, we also start writeback!

Shouldnt that be "if we dont have migrate_page(), start writeback, since in this case well use migrate_page_common() anyway."

It seems the logic is inverted, or maybe I'm wrong.

It's my understanding that the previous code is equivalent to the following code. Am I missing something?

if (PageDirty(page) && PagePrivate(page)) {
        if (!TestSetPageLocked(page)) {
                mapping = page_mapping(page);
                if (!mapping) {
                        unlock_page(page);
		} else if (mapping->a_ops->migrate_page) {
                        unlock_page(page);
		} else if (pageout(page, mapping) != PAGE_SUCCESS) {
                        unlock_page(page);
		}
	}
}
Quoted 11 lines
Quoted 1 line

As you may know the migration code will work fine without this code.

OK!

Quoted 1 line
Quoted 1 line

That is, if we can't migrate the page, try to write it out?

I just didnt understand the logic very well, maybe I should just go reread the code.

Thanks!

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#22 lore raw
Quoted 48 lines · Hirokazu Takahashi

Hi, Marcelo.

Quoted 36 lines · Hirokazu Takahashi
Quoted 27 lines

Hi, Marcelo.

Quoted 23 lines

It seems there is typo in the current version of the patch:

int try_to_migrate_pages(struct list_head *page_list) {
...

        current->flags |= PF_KSWAPD;    /*  It's fake */
        list_for_each_entry_safe(page, page2, page_list, lru) {
                /*
                 * Start writeback I/O if it's a dirty page with buffers
                 * and it doesn't have migrate_page method.
                 */
                if (PageDirty(page) && PagePrivate(page)) {
                        if (!TestSetPageLocked(page)) {
                                mapping = page_mapping(page);
                                if (!mapping || mapping->a_ops->migrate_page ||
						^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                    pageout(page, mapping) != PAGE_SUCCESS) {
                                        unlock_page(page);
                                }
                        }
                }
Quoted 1 line

Shouldnt that be "!mapping->a_ops->migrate_page"?

Quoted 7 lines

"mapping->a_ops->migrate_page" is correct.

This code is just for optimization. If mapping->a_ops->migrate_page isn't implemented, migrate_page_common() is used to migrate the page. migrate_page_common() will try to write it back if it's dirty, so that it would be better to start writeback I/O for target pages in advance without waiting the I/O completions.

Quoted 6 lines

Right. But if migrate_page _is_ implemented, we also start writeback!

Shouldnt that be "if we dont have migrate_page(), start writeback, since in this case well use migrate_page_common() anyway."

It seems the logic is inverted, or maybe I'm wrong.

It's my understanding that the previous code is equivalent to the following code. Am I missing something?

Quoted 12 lines
if (PageDirty(page) && PagePrivate(page)) {
        if (!TestSetPageLocked(page)) {
                mapping = page_mapping(page);
                if (!mapping) {
                        unlock_page(page);
		} else if (mapping->a_ops->migrate_page) {
                        unlock_page(page);
		} else if (pageout(page, mapping) != PAGE_SUCCESS) {
                        unlock_page(page);
		}
	}
}

OK I'm wrong.

Quoted 10 lines
Quoted 10 lines
Quoted 1 line

As you may know the migration code will work fine without this code.

OK!

Quoted 1 line
Quoted 1 line

That is, if we can't migrate the page, try to write it out?

I just didnt understand the logic very well, maybe I should just go reread the code.

Thanks!

I'm thinking about how to implement a nonblocking version of generic_migrate_page().

For this purpose its really bad to allocate swap space to anonymous pages, well need to figure out someother way of blocking the users via pagetablefault.

Like a "virtual" swap space but without allocating swap map space.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#23 lore raw

Hi, Marcelo.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 1 line
Quoted 1 line

That is, if we can't migrate the page, try to write it out?

I just didnt understand the logic very well, maybe I should just go reread the code.

Quoted 8 lines
Quoted 1 line
Quoted 1 line

Thanks!

I'm thinking about how to implement a nonblocking version of generic_migrate_page().

For this purpose its really bad to allocate swap space to anonymous pages, well need to figure out someother way of blocking the users via pagetablefault.

Like a "virtual" swap space but without allocating swap map space.

I've also ever thought to implement such a device. It would be nice if you can design it simple.

Mr.Iwamoto thought otherwise and posted another opinion on the lhms list, though. I felt it also has a point.

iwamoto> I don't think requiring swap is a big deal. If you don't have a iwamoto> dedicated swap device, which case I think unusual, you can swapon a iwamoto> regular file.

Thanks,
Hirokazu Takahashi.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#24 lore raw
Quoted 19 lines · Hirokazu Takahashi

Hi, Marcelo.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 1 line
Quoted 1 line

That is, if we can't migrate the page, try to write it out?

I just didnt understand the logic very well, maybe I should just go reread the code.

Quoted 8 lines
Quoted 1 line
Quoted 1 line

Thanks!

I'm thinking about how to implement a nonblocking version of generic_migrate_page().

For this purpose its really bad to allocate swap space to anonymous pages, well need to figure out someother way of blocking the users via pagetablefault.

Like a "virtual" swap space but without allocating swap map space.

I've also ever thought to implement such a device. It would be nice if you can design it simple.

Quoted 6 lines

Mr.Iwamoto thought otherwise and posted another opinion on the lhms list, though. I felt it also has a point.

iwamoto> I don't think requiring swap is a big deal. If you don't have a iwamoto> dedicated swap device, which case I think unusual, you can swapon a iwamoto> regular file.

Sure its not a big deal, but nicer if it doesnt require swap.

For memory defragmentation it is a big deal.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#25 lore raw

At Fri, 8 Oct 2004 12:36:46 -0300,

Quoted 24 lines · Marcelo Tosatti
Quoted 22 lines · Hirokazu Takahashi

Hi, Marcelo.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 1 line
Quoted 1 line

That is, if we can't migrate the page, try to write it out?

I just didnt understand the logic very well, maybe I should just go reread the code.

Quoted 8 lines
Quoted 1 line
Quoted 1 line

Thanks!

I'm thinking about how to implement a nonblocking version of generic_migrate_page().

For this purpose its really bad to allocate swap space to anonymous pages, well need to figure out someother way of blocking the users via pagetablefault.

Like a "virtual" swap space but without allocating swap map space.

I've also ever thought to implement such a device. It would be nice if you can design it simple.

Mr.Iwamoto thought otherwise and posted another opinion on the lhms list, though. I felt it also has a point.

Quoted 7 lines
Quoted 3 lines

iwamoto> I don't think requiring swap is a big deal. If you don't have a iwamoto> dedicated swap device, which case I think unusual, you can swapon a iwamoto> regular file.

Sure its not a big deal, but nicer if it doesnt require swap.

For memory defragmentation it is a big deal.

Why? IMO, it isn't very rewarding to tune memory migration/defragmentation performance as they involve memory copy anyway.

Or, do you want memory defragmentation everywhere, including embedded systems?

--
IWAMOTO Toshihiro
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#26 lore raw
Quoted 30 lines · IWAMOTO Toshihiro

At Fri, 8 Oct 2004 12:36:46 -0300,

Quoted 28 lines · Marcelo Tosatti
Quoted 25 lines · Hirokazu Takahashi

Hi, Marcelo.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 1 line
Quoted 1 line

That is, if we can't migrate the page, try to write it out?

I just didnt understand the logic very well, maybe I should just go reread the code.

Quoted 8 lines
Quoted 1 line
Quoted 1 line

Thanks!

I'm thinking about how to implement a nonblocking version of generic_migrate_page().

For this purpose its really bad to allocate swap space to anonymous pages, well need to figure out someother way of blocking the users via pagetablefault.

Like a "virtual" swap space but without allocating swap map space.

I've also ever thought to implement such a device. It would be nice if you can design it simple.

Mr.Iwamoto thought otherwise and posted another opinion on the lhms list, though. I felt it also has a point.

iwamoto> I don't think requiring swap is a big deal. If you don't have a iwamoto> dedicated swap device, which case I think unusual, you can swapon a iwamoto> regular file.

Quoted 7 lines
Quoted 3 lines

Sure its not a big deal, but nicer if it doesnt require swap.

For memory defragmentation it is a big deal.

Why? IMO, it isn't very rewarding to tune memory migration/defragmentation performance as they involve memory copy anyway.

Hi Iwamoto,

Oh yes, they already involve memory copy, but then if they use swap its worse!

Quoted 2 lines

Or, do you want memory defragmentation everywhere, including embedded systems?

Yes I want defragmentation everywhere!

The thing is grabbing swap pages for memory migration is not a very optimal operation.

First, we interfere with swap allocation patterns (if true swap is going on at the moment, we screw up is performance).

We are probably also not going to use that swap space, there is no point in allocating it.

And finally if we run out of swap space, we are dead.

Sure, it works fine with all this restrictions, but it would be an advantage if we didnt had such swap usage overhead for memory migration.

I'm writting a "migration cache" - its basically a swapcache without backing store, instead we use idr (lib/idr.c) to allocate the offsets.

It will be much faster and not interfere with swap space.

I'll use one bit of "swap type" to identify such "migration pte's".

I'll test it with memory migration operation first then with memory defragmentation.

Hope it works fine.

struct idr migration_idr;
struct address_space migration_space = {

        .page_tree      = RADIX_TREE_INIT(GFP_ATOMIC),
        .tree_lock      = RW_LOCK_UNLOCKED,
        .a_ops          = NULL,
        .flags          = GFP_HIGHUSER,

.i_mmap_nonlinear = LIST_HEAD_INIT(migration_space.i_mmap_nonlinear), .backing_dev_info = NULL,
};

int init_migration_cache(void)

{
	idr_init(&migration_idr);

	printk(KERN_INFO "Initializating migration cache!\n");

}

__initcall(init_migration_cache);

struct page *lookup_migration_cache(int id) { 
	return find_get_page(&migration_space, id);
}

int remove_from_migration_cache(struct page *page, int id)

{
	write_lock_irq(&migration_space.tree_lock);
        idr_remove(&migration_idr, id);
	radix_tree_delete(&migration_space.page_tree, id);
	write_unlock_irq(&migration_space.tree_lock);
}

int add_to_migration_cache(struct page *page)

{
	int error, offset;
	int gfp_mask = GFP_KERNEL;

	BUG_ON(PageSwapCache(page));
	BUG_ON(PagePrivate(page));

        if (idr_pre_get(&migration_idr, GFP_ATOMIC) == 0)
                return -ENOMEM;

	error = radix_tree_preload(gfp_mask);

	if (!error) {
		write_lock_irq(&migration_space.tree_lock);
	        error = idr_get_new(&migration_idr, NULL, &offset);

		error = radix_tree_insert(&migration_space.page_tree, offset,
							page);

		if (!error) {
			page_cache_get(page);
			SetPageLocked(page);
			page->private = offset;
			page->mapping = &migration_space;
		}
		write_unlock_irq(&migration_cache.tree_lock);
                radix_tree_preload_end();

	}

	return error;
}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#27 lore raw

Hi,

Quoted 8 lines

It will be much faster and not interfere with swap space.

I'll use one bit of "swap type" to identify such "migration pte's".

I'll test it with memory migration operation first then with memory defragmentation.

Hope it works fine.

IMHO, if one "swap type" is reserved for it, a target page can be inserted in swapper_space directly. I guess it may be possible to reuse the swapper_space and some of the existing codes, which should be moved out of #ifdef CONFIG_SWAP though.

Quoted 33 lines

struct idr migration_idr;
struct address_space migration_space = {

        .page_tree      = RADIX_TREE_INIT(GFP_ATOMIC),
        .tree_lock      = RW_LOCK_UNLOCKED,
        .a_ops          = NULL,
        .flags          = GFP_HIGHUSER,

.i_mmap_nonlinear = LIST_HEAD_INIT(migration_space.i_mmap_nonlinear), .backing_dev_info = NULL,
};

int init_migration_cache(void)

{
	idr_init(&migration_idr);

	printk(KERN_INFO "Initializating migration cache!\n");

}

__initcall(init_migration_cache);

struct page *lookup_migration_cache(int id) { 
	return find_get_page(&migration_space, id);
}

int remove_from_migration_cache(struct page *page, int id)

{
	write_lock_irq(&migration_space.tree_lock);
        idr_remove(&migration_idr, id);
	radix_tree_delete(&migration_space.page_tree, id);
	write_unlock_irq(&migration_space.tree_lock);
}

int add_to_migration_cache(struct page *page)

Quoted 9 lines
{
	int error, offset;
	int gfp_mask = GFP_KERNEL;

	BUG_ON(PageSwapCache(page));
	BUG_ON(PagePrivate(page));

        if (idr_pre_get(&migration_idr, GFP_ATOMIC) == 0)
                return -ENOMEM;

I guess GFP_KERNEL is enough.

Quoted 14 lines
	error = radix_tree_preload(gfp_mask);

	if (!error) {
		write_lock_irq(&migration_space.tree_lock);
	        error = idr_get_new(&migration_idr, NULL, &offset);

		error = radix_tree_insert(&migration_space.page_tree, offset,
							page);

		if (!error) {
			page_cache_get(page);
			SetPageLocked(page);
			page->private = offset;
			page->mapping = &migration_space;
Quoted 9 lines
		}
		write_unlock_irq(&migration_cache.tree_lock);
                radix_tree_preload_end();

	}

	return error;
}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#28 lore raw
In reply to IWAMOTO Toshihiro
Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

iwamoto> I don't think requiring swap is a big deal. If you don't have a iwamoto> dedicated swap device, which case I think unusual, you can swapon a iwamoto> regular file.

Sure its not a big deal, but nicer if it doesnt require swap.

Quoted 8 lines
Quoted 1 line

For memory defragmentation it is a big deal.

Why? IMO, it isn't very rewarding to tune memory migration/defragmentation performance as they involve memory copy anyway.

Or, do you want memory defragmentation everywhere, including embedded systems?

Lots of systems nowadays don't have swap configured, not just embedded. What do we gain from making defrag slower and harder to use, by forcing it to use swap? Isn't pushing it into the swapcache sufficient?

M.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#29 lore raw
Quoted 12 lines · Martin J. Bligh
Quoted 12 lines
Quoted 7 lines
Quoted 3 lines

iwamoto> I don't think requiring swap is a big deal. If you don't have a iwamoto> dedicated swap device, which case I think unusual, you can swapon a iwamoto> regular file.

Sure its not a big deal, but nicer if it doesnt require swap.

For memory defragmentation it is a big deal.

Why? IMO, it isn't very rewarding to tune memory migration/defragmentation performance as they involve memory copy anyway.

Quoted 6 lines
Quoted 2 lines

Or, do you want memory defragmentation everywhere, including embedded systems?

Lots of systems nowadays don't have swap configured, not just embedded. What do we gain from making defrag slower and harder to use, by forcing it to use swap? Isn't pushing it into the swapcache sufficient?

Hi Martin,

Yes pushing it to swapcache is sufficient - but doing so requires swap map space (the "index" for swapcache pages is retrieved from swap map space position).

As I posted in the other message I'm working on a idr-based cache (migration cache) which should solve things.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#30 lore raw
In reply to Martin J. Bligh
Quoted 3 lines · Martin J. Bligh

Lots of systems nowadays don't have swap configured, not just embedded. What do we gain from making defrag slower and harder to use, by forcing it to use swap? Isn't pushing it into the swapcache sufficient?

For now, with no swap space configured and CONFIG_SWAP=y, no pages will even make it into the swap cache. It'll take more code on top of what we have to get that to work. So, we're sticking with the smallest amount of code that we can for now. We'll fix that up later.

-- Dave

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#31 lore raw
In reply to Marcelo Tosatti

At Sun, 3 Oct 2004 11:07:23 -0300,

Quoted 24 lines · Marcelo Tosatti
Quoted 14 lines · Hirokazu Takahashi
Quoted 14 lines

2)
At migrate_onepage you add anonymous pages which aren't swap allocated to the swap cache

(quoted diff) +10 -0
+       /*
+        * Put the page in a radix tree if it isn't in the tree yet.
+        */
+#ifdef CONFIG_SWAP
+       if (PageAnon(page) && !PageSwapCache(page))
+               if (!add_to_swap(page, GFP_KERNEL)) {
+                       unlock_page(page);
+                       return ERR_PTR(-ENOSPC);
+               }
+#endif /* CONFIG_SWAP */
Quoted 7 lines
Quoted 2 lines

Why's that? You can copy anonymous pages without adding them to swap (thats what the patch I posted does).

The reason is to guarantee that any anonymous page can be migrated anytime. I want to block newly occurred accesses to the page during the migration because it can't be migrated if there remain some references on it by system calls, direct I/O and page faults.

Quoted 7 lines

It would be nice if we could block pte faults in a way such to not need adding each anonymous page to swap. It can be too costly if you have a lot memory and it makes the whole operation dependable on swap size (if you dont have enough swap, you're dead).

Maybe hold mm->page_table_lock (might be too costly in terms of CPU time, but since migration is not a common operation anyway), or create a semaphore?

I chose the swap cache based implementation in order to minimize slowdown of the normal code path. (I thought there's zero code addition on the normal pagefault path when I designed this, but it's no longer true...)

If we can agree on adding a new lock, there might be a better implementation.

--
IWAMOTO Toshihiro
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#32 lore raw
In reply to Hirokazu Takahashi
Quoted 10 lines · Hirokazu Takahashi
Quoted 2 lines

Questions: are there any documents on the memory hotplug userspace tools? Where can I find them?

IBM guys and Fujitsu guys are designing user interface independently. IBM team is implementing memory section hotplug while Fujitsu team try to implement NUMA node hotplug. But both of the designs use regular hot-plug mechanism, which kicks /sbin/hotplug script to control devices via sysfs.

Dave, would you explain about it?

First of all, we're still on the first set of these APIs. So, either we're really, really smart (unlikely) or we have a few revisions an rewrites to go before everybody is happy.

ls /sys/devices/system/memory/ gives you each memory area, with arbitrary numbers like this:
memory0
memory1
memory2
memory8953

We haven't decided whether to make each of those represent a constant sized area, or let them be variable. In any case, there will either be a range inside of each or a global block size something like here:

/sys/devices/system/memory/block_size

Each memory device would have a directory like this:

# ls /sys/devices/system/memory/memory8953/
node -> ../../node/node4 (for the NUMA case)
state
phys_start_addr

To take a memory section offline, you

echo offline > /sys/devices/system/memory/memory8953/state

For now, that takes the section offline by allocating all of its pages and migrating the test. It also removes the sysfs node, triggering a /sbin/hotplug event for the device removal. We might makes this 2 different states in the future (offline and removal). This could also potentially be triggered by hardware alone.

For now, you can also add memory, but it's hackish and will certainly change:

	echo 0x8000000 > /sys/devices/system/memory/probe

will add SECTION_SIZE amount of memory at 2GB. Yes, SECTION_SIZE is hard-coded, but this is only for testing. We'll eventually take ranges and maybe NUMA information into there somehow. Why can't the hardware just do this? It's a long story :)

Signature · 8 lines
Dave Hansen
[email protected]

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#33 lore raw

PATCH 1/1 mhp: transfer dirty tag at radix_tree_replace

In reply to Hirokazu Takahashi
Quoted 15 lines · Hirokazu Takahashi

Hi,

Quoted 13 lines
Quoted 4 lines
Quoted 2 lines

Cool. I'll take a closer look at the relevant parts of memory hotplug patches this weekend, hopefully. See if I can help with testing of these patches too.

Any comments are very welcome.

I have a few comments about the code:

1)
I'm pretty sure you should transfer the radix tree tag at radix_tree_replace(). If for example you transfer a dirty tagged page to another zone, an mpage_writepages() will miss it (because it uses pagevec_lookup_tag(PAGECACHE_DIRTY_TAG)).

Quoted 8 lines
Quoted 4 lines

Should be quite trivial to do (save tags before deleting and set to new entry, all in radix_tree_replace).

My implementation also contained the same bug.

Yes, it's one of the issues to do. The tag should be transferred in radix_tree_replace() as you pointed out. The current implementation sets the tag in set_page_dirty(newpage).

OK, guys, can you test this please?

This transfer the dirty radix tree tag at radix_tree_replace, avoiding a potential miss on tag-lookup. We could also copy all bits representing the valid tags for this node in the radix tree.

But this uses the available interfaces from radix-lib.c. In case a new tag gets added, radix_tree_replace() will have to know about it.

Pretty straightforward.

I still need to figure out how to use Iwamoto's patch to add/remove zone's on the fly (for testing the migration process).

include/linux/radix-tree.h +8 -0
diff -Nur linux-2.6.9-rc2-mm4.mhp.orig/include/linux/radix-tree.h linux-2.6.9-rc2-mm4/include/linux/radix-tree.h
@@ -68,9 +68,17 @@
 radix_tree_replace(struct radix_tree_root *root,
 				unsigned long index, void *item)
 {
+	int dirty;
+
+	dirty = radix_tree_tag_get(root, index, PAGECACHE_TAG_DIRTY);
+
 	if (radix_tree_delete(root, index) == NULL)
 		return -1;
 	radix_tree_insert(root, index, item);
+
+	if (dirty)
+		radix_tree_tag_set(root, index, PAGECACHE_TAG_DIRTY);
+
 	return 0;
 }
 
lib/radix-tree.c +0 -2
diff -Nur linux-2.6.9-rc2-mm4.mhp.orig/lib/radix-tree.c linux-2.6.9-rc2-mm4/lib/radix-tree.c
@@ -443,7 +443,6 @@
 }
 EXPORT_SYMBOL(radix_tree_tag_clear);
 
-#ifndef __KERNEL__	/* Only the test harness uses this at present */
 /**
  *	radix_tree_tag_get - get a tag on a radix tree node
  *	@root:		radix tree root
@@ -495,7 +494,6 @@
 	}
 }
 EXPORT_SYMBOL(radix_tree_tag_get);
-#endif
 
 static unsigned int
 __lookup(struct radix_tree_root *root, void **results, unsigned long index,

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#34 lore raw

Re: [PATCH] mhp: transfer dirty tag at radix_tree_replace

Quoted 2 lines · Marcelo Tosatti

I still need to figure out how to use Iwamoto's patch to add/remove zone's on the fly (for testing the migration process).

What I do (on my machine with 4G of RAM) is boot with mem=2G, then

	echo 0x8000000 > /sys/devices/system/memory/probe

which onlines 128MB more at 2GB. Then, I allocate about 2GB of memory with some app. And this:

echo offline > /sys/devices/system/memory/memory16/state

starts the migration process.

Hirokazu, if you want to send me your latest patch set against the tree that I just posted, I'll start to merge it up for real.

-- Dave

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#35 lore raw

Re: [PATCH] mhp: transfer dirty tag at radix_tree_replace

In reply to Marcelo Tosatti

Hi, Marcelo.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

1)
I'm pretty sure you should transfer the radix tree tag at radix_tree_replace(). If for example you transfer a dirty tagged page to another zone, an mpage_writepages() will miss it (because it uses pagevec_lookup_tag(PAGECACHE_DIRTY_TAG)).

Should be quite trivial to do (save tags before deleting and set to new entry, all in radix_tree_replace).

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

My implementation also contained the same bug.

Yes, it's one of the issues to do. The tag should be transferred in radix_tree_replace() as you pointed out. The current implementation sets the tag in set_page_dirty(newpage).

OK, guys, can you test this please?

Ok, I'll test it.

Quoted 6 lines

This transfer the dirty radix tree tag at radix_tree_replace, avoiding a potential miss on tag-lookup. We could also copy all bits representing the valid tags for this node in the radix tree.

But this uses the available interfaces from radix-lib.c. In case a new tag gets added, radix_tree_replace() will have to know about it.

Yeah. I guess it would be better to copy the radix_tree_delete() code to radix_tree_replace() and modify it to replace items directly in the future.

Quoted 46 lines

Pretty straightforward.

I still need to figure out how to use Iwamoto's patch to add/remove zone's on the fly (for testing the migration process).

diff -Nur linux-2.6.9-rc2-mm4.mhp.orig/include/linux/radix-tree.h linux-2.6.9-rc2-mm4/include/linux/radix-tree.h

include/linux/radix-tree.h +8 -0
@@ -68,9 +68,17 @@
 radix_tree_replace(struct radix_tree_root *root,
 				unsigned long index, void *item)
 {
+	int dirty;
+
+	dirty = radix_tree_tag_get(root, index, PAGECACHE_TAG_DIRTY);
+
 	if (radix_tree_delete(root, index) == NULL)
 		return -1;
 	radix_tree_insert(root, index, item);
+
+	if (dirty)
+		radix_tree_tag_set(root, index, PAGECACHE_TAG_DIRTY);
+
 	return 0;
 }
 

diff -Nur linux-2.6.9-rc2-mm4.mhp.orig/lib/radix-tree.c linux-2.6.9-rc2-mm4/lib/radix-tree.c

lib/radix-tree.c +0 -3
@@ -443,7 +443,6 @@
 }
 EXPORT_SYMBOL(radix_tree_tag_clear);
 
-#ifndef __KERNEL__	/* Only the test harness uses this at present */
 /**
  *	radix_tree_tag_get - get a tag on a radix tree node
  *	@root:		radix tree root
@@ -495,7 +494,6 @@
 	}
 }
 EXPORT_SYMBOL(radix_tree_tag_get);
-#endif
 
 static unsigned int
 __lookup(struct radix_tree_root *root, void **results, unsigned long index,
--
Quoted 5 lines

To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#36 lore raw

Re: [PATCH] mhp: transfer dirty tag at radix_tree_replace

Hi, Marcelo.

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

1)
I'm pretty sure you should transfer the radix tree tag at radix_tree_replace(). If for example you transfer a dirty tagged page to another zone, an mpage_writepages() will miss it (because it uses pagevec_lookup_tag(PAGECACHE_DIRTY_TAG)).

Should be quite trivial to do (save tags before deleting and set to new entry, all in radix_tree_replace).

My implementation also contained the same bug.

Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

Yes, it's one of the issues to do. The tag should be transferred in radix_tree_replace() as you pointed out. The current implementation sets the tag in set_page_dirty(newpage).

OK, guys, can you test this please?

Ok, I'll test it.

It was sad that the patch couldn't be compiled because PAGECACHE_TAG_DIRTY macro depended on the filesystem code. I think radix_tree library shouldn't use it.

So that it would be better to make radix_tree_replace() accept tags to be inherited or make the function from scratch.
So I decided to re-implement it and I'm testing it now.

Quoted 10 lines
Quoted 6 lines

This transfer the dirty radix tree tag at radix_tree_replace, avoiding a potential miss on tag-lookup. We could also copy all bits representing the valid tags for this node in the radix tree.

But this uses the available interfaces from radix-lib.c. In case a new tag gets added, radix_tree_replace() will have to know about it.

Yeah. I guess it would be better to copy the radix_tree_delete() code to radix_tree_replace() and modify it to replace items directly in the future.

void *radix_tree_replace(struct radix_tree_root *root,
unsigned long index, void *item)

{
        struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path;
        unsigned int height, shift;
        void *ret = NULL;

        height = root->height;
        if (index > radix_tree_maxindex(height))
                goto out;

        shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
        pathp->node = NULL;
        pathp->slot = &root->rnode;

        while (height > 0) {
                int offset;

                if (*pathp->slot == NULL)
                        goto out;

                offset = (index >> shift) & RADIX_TREE_MAP_MASK;
                pathp[1].offset = offset;
                pathp[1].node = *pathp[0].slot;
                pathp[1].slot = (struct radix_tree_node **)
                                (pathp[1].node->slots + offset);
                pathp++;
                shift -= RADIX_TREE_MAP_SHIFT;
                height--;
        }

        if ((ret = *pathp[0].slot))
                *pathp[0].slot = item;
out:
        return ret;
}

Thank you,
Hirokazu Takahashi.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#37 lore raw

Re: [PATCH] mhp: transfer dirty tag at radix_tree_replace

Quoted 69 lines · Hirokazu Takahashi

Hi, Marcelo.

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

1)
I'm pretty sure you should transfer the radix tree tag at radix_tree_replace(). If for example you transfer a dirty tagged page to another zone, an mpage_writepages() will miss it (because it uses pagevec_lookup_tag(PAGECACHE_DIRTY_TAG)).

Should be quite trivial to do (save tags before deleting and set to new entry, all in radix_tree_replace).

My implementation also contained the same bug.

Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

Yes, it's one of the issues to do. The tag should be transferred in radix_tree_replace() as you pointed out. The current implementation sets the tag in set_page_dirty(newpage).

OK, guys, can you test this please?

Ok, I'll test it.

It was sad that the patch couldn't be compiled because PAGECACHE_TAG_DIRTY macro depended on the filesystem code. I think radix_tree library shouldn't use it.

So that it would be better to make radix_tree_replace() accept tags to be inherited or make the function from scratch.
So I decided to re-implement it and I'm testing it now.

Quoted 10 lines
Quoted 6 lines

This transfer the dirty radix tree tag at radix_tree_replace, avoiding a potential miss on tag-lookup. We could also copy all bits representing the valid tags for this node in the radix tree.

But this uses the available interfaces from radix-lib.c. In case a new tag gets added, radix_tree_replace() will have to know about it.

Yeah. I guess it would be better to copy the radix_tree_delete() code to radix_tree_replace() and modify it to replace items directly in the future.

void *radix_tree_replace(struct radix_tree_root *root,
unsigned long index, void *item)

{
        struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path;
        unsigned int height, shift;
        void *ret = NULL;

        height = root->height;
        if (index > radix_tree_maxindex(height))
                goto out;

        shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
        pathp->node = NULL;
        pathp->slot = &root->rnode;

        while (height > 0) {
                int offset;

                if (*pathp->slot == NULL)
                        goto out;

                offset = (index >> shift) & RADIX_TREE_MAP_MASK;
                pathp[1].offset = offset;
                pathp[1].node = *pathp[0].slot;
                pathp[1].slot = (struct radix_tree_node **)
                                (pathp[1].node->slots + offset);
                pathp++;
                shift -= RADIX_TREE_MAP_SHIFT;
Quoted 8 lines
                height--;
        }

        if ((ret = *pathp[0].slot))
                *pathp[0].slot = item;
out:
        return ret;
}

I'm not too familiar with the radix internals, but this looks fine to me.

Thanks Hirokazu!
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#38 lore raw
In reply to Marcelo Tosatti

At Sat, 2 Oct 2004 15:33:49 -0300,

Quoted 11 lines · Marcelo Tosatti

On Sat, Oct 02, 2004 at 06:30:15PM +0900, Hirokazu Takahashi wrote:
3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Is that assumption always valid?

In theory there is no need to writeout pages when migrating them to other zones - they will be copied and the dirty information retained (either in the PageDirty bit or radix tree tag).

It's true only when page->private is NULL. Otherwise writeback is necessary to free buffer_head.

Quoted 5 lines

I just noticed you do that on further patches (migrate_page_buffer), but AFAICS the writeout remains. Why arent you using migrate_page_buffer yet?

I think the final aim should be to remove the need for "pageout()" completly.

Are you going to implement migrate_page_buffer for every file system? I don't think it's worthwhile.

Quoted 4 lines

Questions: are there any documents on the memory hotplug userspace tools? Where can I find them?

Are Iwamoto's test programs available?

I've put them at the following URL, but I doubt they are useful for you; there are no documentation for them.

http://people.valinux.co.jp/~iwamoto/mh/tests/

--
IWAMOTO Toshihiro
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#39 lore raw
Quoted 8 lines · IWAMOTO Toshihiro

At Sat, 2 Oct 2004 15:33:49 -0300,

Quoted 6 lines · Marcelo Tosatti

On Sat, Oct 02, 2004 at 06:30:15PM +0900, Hirokazu Takahashi wrote:
3) At migrate_page_common you assume additional page references (page_migratable returning -EAGAIN) means the code should try to writeout the page.

Quoted 8 lines
Quoted 5 lines

Is that assumption always valid?

In theory there is no need to writeout pages when migrating them to other zones - they will be copied and the dirty information retained (either in the PageDirty bit or radix tree tag).

It's true only when page->private is NULL. Otherwise writeback is necessary to free buffer_head.

You can move the buffer_head's also cant you? Adjusting bh->b_page etc.

Thats what migrate_page_buffer does, no?

Writting pages which contain buffer_head's on memory migration is really, very bad.

Imagine gigabytes of pages with buffer_head's.

Quoted 12 lines
Quoted 5 lines

I just noticed you do that on further patches (migrate_page_buffer), but AFAICS the writeout remains. Why arent you using migrate_page_buffer yet?

I think the final aim should be to remove the need for "pageout()" completly.

Are you going to implement migrate_page_buffer for every file system? I don't think it's worthwhile.

Quoted 3 lines

Questions: are there any documents on the memory hotplug userspace tools? Where can I find them?

Quoted 6 lines
Quoted 1 line

Are Iwamoto's test programs available?

I've put them at the following URL, but I doubt they are useful for you; there are no documentation for them.

http://people.valinux.co.jp/~iwamoto/mh/tests/

I'll take a look thanks.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#40 lore raw
In reply to Marcelo Tosatti
Quoted 6 lines · Marcelo Tosatti

With such a thing in place we can build a mechanism for kswapd (or a separate kernel thread, if needed) to notice when we are low on high order pages, and use the coalescing algorithm instead blindly freeing unique pages from LRU in the hope to build large physically contiguous memory areas.

Comments appreciated.

Hi Marcelo,
Seems like a good idea... even with regular dumb kswapd "merging", you may easily get stuck for example on systems without swap...

Anyway, I'd like to get those beat kswapd patches in first. Then your mechanism just becomes something like:

   if order-0 pages are low {
       try to free memory
   }
   else if order-1 or higher pages are low {
        try to coalesce_memory
        if that fails, try to free memory
   }

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#41 lore raw
Quoted 22 lines · Nick Piggin
Quoted 10 lines · Marcelo Tosatti

With such a thing in place we can build a mechanism for kswapd (or a separate kernel thread, if needed) to notice when we are low on high order pages, and use the coalescing algorithm instead blindly freeing unique pages from LRU in the hope to build large physically contiguous memory areas.

Comments appreciated.

Hi Marcelo,
Seems like a good idea... even with regular dumb kswapd "merging", you may easily get stuck for example on systems without swap...

Anyway, I'd like to get those beat kswapd patches in first. Then your mechanism just becomes something like:

Quoted 7 lines
   if order-0 pages are low {
       try to free memory
   }
   else if order-1 or higher pages are low {
        try to coalesce_memory
        if that fails, try to free memory
   }

Hi Nick!

I understand that kswapd is broken, and it needs to go into the page reclaim path to free pages when we are out of high order pages (what your
"beat kswapd" patches do and fix high-order failures by doing so), but Linus's argument against it seems to be that "it potentially frees too much pages" causing harm to the system. He also says this has been tried in the past, with not nice results.

And that is why its has not been merged into mainline.

Is my interpretation correct?

But right, kswapd needs to get fixed to honour high order pages.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#42 lore raw
Quoted 28 lines · Marcelo Tosatti
Quoted 17 lines · Nick Piggin
Quoted 6 lines · Marcelo Tosatti

With such a thing in place we can build a mechanism for kswapd (or a separate kernel thread, if needed) to notice when we are low on high order pages, and use the coalescing algorithm instead blindly freeing unique pages from LRU in the hope to build large physically contiguous memory areas.

Comments appreciated.

Hi Marcelo,
Seems like a good idea... even with regular dumb kswapd "merging", you may easily get stuck for example on systems without swap...

Anyway, I'd like to get those beat kswapd patches in first. Then your mechanism just becomes something like:

Quoted 8 lines
  if order-0 pages are low {
      try to free memory
  }
  else if order-1 or higher pages are low {
       try to coalesce_memory
       if that fails, try to free memory
  }
Quoted 3 lines

Hi Nick!

Sorry, I'd been away for the weekend which is why I didn't get a chance to reply to you.

Quoted 3 lines

I understand that kswapd is broken, and it needs to go into the page reclaim path to free pages when we are out of high order pages (what your "beat kswapd" patches do and fix high-order failures by doing so), but Linus's argument against it seems to be that "it potentially frees too much pages" causing harm to the system. He also says this has been tried in the past, with not nice results.

Not quite. I think a (the) big thing with my patch is that it will check order-0...n watermarks when an order-n allocation is made.

So if there is no order >2 allocations happening, it won't attempt to keep higher order memory available (until someone attempts an allocation).

Basically, it gets kswapd doing the work when it would otherwise have to be done in direct reclaim, *OR* otherwise indefinitely fail if the allocations aren't blockable.

Quoted 8 lines

And that is why its has not been merged into mainline.

Is my interpretation correct?

But right, kswapd needs to get fixed to honour high order pages.

Well Linus was silent on the issue after I answered his concerns. I mailed him privately and he basically said that it seems sane, and he is waiting for patches. Of course, by that stage it was fairly late into 2.6.9, and the current behaviour isn't a regression, so I'm shooting for 2.6.10.

Your defragmentor should sit very nicely on top of it, of course.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#43 lore raw
In reply to Marcelo Tosatti
Quoted 4 lines · Marcelo Tosatti

For example it doesnt re establishes pte's once it has unmapped them.

Another thing - I don't know if I'd bother re-establishing ptes.... I'd say just leave it to happen lazily at fault time.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#44 lore raw

Hello,

Quoted 6 lines
Quoted 3 lines

For example it doesnt re establishes pte's once it has unmapped them.

Another thing - I don't know if I'd bother re-establishing ptes.... I'd say just leave it to happen lazily at fault time.

I think the reason is that his current implementation doesn't assign a swap entry to an anonymous page to move.

Thank you,
Hirokazu Takahashi.

#45 lore raw
In reply to Nick Piggin
Quoted 11 lines · Nick Piggin
Quoted 4 lines · Marcelo Tosatti

For example it doesnt re establishes pte's once it has unmapped them.

Another thing - I don't know if I'd bother re-establishing ptes.... I'd say just leave it to happen lazily at fault time.

Indeed it should work lazily.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#46 lore raw
In reply to Marcelo Tosatti

how about inserting this if-sentense ?

-- Kame

Quoted 2 lines · Marcelo Tosatti
page_alloc.c renamed from page_alloc.c.orig +2 -0
+int coalesce_memory(unsigned int order, struct zone *zone)
+{

<snip>

page_alloc.c line 507

Quoted 5 lines
page_alloc.c renamed from page_alloc.c.orig +5 -0
+		while (entry != &area->free_list) {
+			int ret;
+			page = list_entry(entry, struct page, lru);
+			entry = entry->next;
+

+ if (((page_to_pfn(page) - zone->zone_start_pfn) & (1 << toorder)) {

page_alloc.c line 512

Quoted 5 lines
page_alloc.c renamed from page_alloc.c.orig +5 -0
+			pwalk = page;
+
+			/* Look backwards */
+
+			for (walkcount = 1; walkcount<nr_pages; walkcount++) {
                        ..................

Review

Quoted 2 lines
(quoted diff) +2 -0
+			}
+

+ } else {

Quoted 12 lines
page_alloc.c renamed from page_alloc.c.orig +12 -0
+forward:
+
+			pwalk = page;
+
+			/* Look forward, skipping the page frames from this 
+			  high order page we are looking at */
+
+			for (walkcount = (1UL << torder); walkcount<nr_pages; 
+					walkcount++) {
+				pwalk = page+walkcount;
+
+				ret = can_move_page(pwalk);

page_alloc.c line 547

Quoted 10 lines
page_alloc.c renamed from page_alloc.c.orig +10 -0
+
+				if (ret) 
+					nr_freed_pages++;
+				else
+					goto loopey;
+
+				if (nr_freed_pages == nr_pages)
+					goto success;
+			}
+

+ }

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#48 lore raw
In reply to Marcelo Tosatti

Hi,

Quoted 2 lines · Marcelo Tosatti
page_alloc.c renamed from page_alloc.c.orig +2 -0
+int can_move_page(struct page *page) 
+{

<snip>

page_alloc.c line 289

Quoted 2 lines
page_alloc.c renamed from page_alloc.c.orig +2 -0
+	if (page_count(page) == 0)
+		return 1;

I think there are 3 cases when page_count(page) == 0.

1. a page is free and in the buddy allocator.
2. a page is free and in per-cpu-pages list.
3. a page is in pagevec .

I think only case 1 pages meet your requirements.

I used PG_private flag for distinguishing case 1 from 2 and 3 in my no-bitmap buddy allocator posted before.
I added PG_private flag to a page which is in buddy allocator's free_list.

Regards

-- Kame
<[email protected]>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#49 lore raw

memory hotplug and mem=

Hi memory hotplug fellows,

Just in case you dont know, trying to pass "mem="
causes the -test2 tree to oops on boot.

Any ideas of what is going on wrong?

Haven't captured the oops, but can
if needed.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#50 lore raw

Re: memory hotplug and mem=

Quoted 6 lines · Marcelo Tosatti

Hi memory hotplug fellows,

Just in case you dont know, trying to pass "mem="
causes the -test2 tree to oops on boot.

Any ideas of what is going on wrong?

Nope. That's my normal mode of operation. What kind of system? How much RAM?

I've only tried it where the machine has 4G of ram, and I restrict it down to 2. I can imagine some funny stuff happening if the mem= causes it to cross the highmem boundary.

Quoted 2 lines

Haven't captured the oops, but can
if needed.

Let me do a bit of testing after I find out what your configuration is. I'm hopeful I can reproduce it.

-- Dave

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#51 lore raw

Re: memory hotplug and mem=

Quoted 10 lines · Dave Hansen
Quoted 6 lines · Marcelo Tosatti

Hi memory hotplug fellows,

Just in case you dont know, trying to pass "mem="
causes the -test2 tree to oops on boot.

Any ideas of what is going on wrong?

Nope. That's my normal mode of operation. What kind of system? How much RAM?

standard P4 desktop system - 512M.

Quoted 3 lines

I've only tried it where the machine has 4G of ram, and I restrict it down to 2. I can imagine some funny stuff happening if the mem= causes it to cross the highmem boundary.

No highmem involved at all.

Quoted 5 lines
Quoted 2 lines

Haven't captured the oops, but can
if needed.

Let me do a bit of testing after I find out what your configuration is. I'm hopeful I can reproduce it.

mem=128M or mem=256M made it crash. Keep me posted.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#52 lore raw

Re: memory hotplug and mem=

Quoted 1 line · Marcelo Tosatti

mem=128M or mem=256M made it crash. Keep me posted.

So far, I've only tested it with highmem. Your config will take some tweaking because vmalloc space fills up all of lowmem if you don't use it for ZONE_NORMAL at boot-time. I'll work on fixing that. But, you're almost certainly going to be stuck adding the new memory to highmem, no matter that it's really in the lower 1GB of physical memory. Otherwise, we're going to have to screw with vmalloc space.

-- Dave

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#53 lore raw

Re: memory hotplug and mem=

In reply to Marcelo Tosatti

Turns out that there are a few invalid uses of pfn_to_page() in the normal code. If you hand-apply the following chunks, it gets a bit farther into boot. These aren't permanent fixes. We really need a last_lowmem_pfn instead of highstart_pfn.

static void __init set_max_mapnr_init(void)
{
#ifdef CONFIG_HIGHMEM
- highmem_start_page = pfn_to_page(highstart_pfn);

+       highmem_start_page = pfn_to_page(highstart_pfn-1);
        max_mapnr = num_physpages = highend_pfn;
 #else
+++ memhotplug-dave/arch/i386/mm/pageattr.c     2004-10-07 13:22:41.000000000 -0700
@@ -109,7 +109,7 @@ __change_page_attr(struct page *page, pg
        struct page *kpte_page;

#ifdef CONFIG_HIGHMEM
- if (page >= highmem_start_page)
+ if (page > highmem_start_page)

                BUG();

#endif

-- Dave

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>