Skip to contentSkip to conversation

mm: swapoff prototype: frontswap handling added

RFC In review Kelley Nielsen 4 participants 4 messages Mar 21, 2015 – Apr 22, 2015

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

RFC v7 2/2 mm: swapoff prototype: frontswap handling added

The prototype of the new swapoff (without the quadratic complexity) presently ignores the frontswap case. Pass the count of pages_to_unuse down the page table walks in try_to_unuse(), and return from the walk when the desired number of pages has been swapped back in.

---
Changes since v6:

- In try_to_unuse(), copy pages_to_unuse to local fs_pages_to_unuse and pass by reference (instead of value) down the page table walk
- create #define FRONTSWAP_PAGES_UNUSED to pass special code back up the chain from unuse_pte_range()
- Test for this return code in try_to_unuse() and exit before orphans cleanup and retries
- In unuse_pte_range, move decrement of frontswap pages count next to call to delete_from_swap_cache(), so that it happens on every unused swap slot instead of every pte unmap
- Remove unnecessary goto after call to unuse_mm() in try_to_unuse()
- Remove blank line in shmem.c/shmem_unuse_inode()

---
 mm/swapfile.c | 60 +++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 42 insertions(+), 18 deletions(-)
mm/swapfile.c +42 -18
index 79c47b6..e4f5289 100644
@@ -1144,9 +1144,11 @@ out_nolock:
 	return ret;
 }
 
+#define FRONTSWAP_PAGES_UNUSED 2
 static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 				unsigned long addr, unsigned long end,
-				unsigned int type)
+				unsigned int type,
+				unsigned long *fs_pages_to_unuse)
 {
 	struct page *page;
 	swp_entry_t entry;
@@ -1169,6 +1171,8 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 			continue;
 		if (found_type != type)
 			continue;
+		if ((*fs_pages_to_unuse > 0) && (!frontswap_test(si, offset)))
+				continue;
 
 		swap_map = &si->swap_map[offset];
 		if (!swap_count(*swap_map))
@@ -1205,11 +1209,18 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 		if (PageSwapCache(page) && (swap_count(*swap_map) == 0)) {
 			wait_on_page_writeback(page);
 			delete_from_swap_cache(page);
+			if (*fs_pages_to_unuse > 0) {
+				(*fs_pages_to_unuse)--;
+				if (*fs_pages_to_unuse == 0)
+					ret = FRONTSWAP_PAGES_UNUSED;
+			}
 		}
 
 		SetPageDirty(page);
 		unlock_page(page);
 		page_cache_release(page);
+		if (ret == FRONTSWAP_PAGES_UNUSED)
+			goto out;
 try_next:
 		pte = pte_offset_map(pmd, addr);
 	} while (pte++, addr += PAGE_SIZE, addr != end);
@@ -1220,7 +1231,8 @@ out:
 
 static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
 				unsigned long addr, unsigned long end,
-				unsigned int type)
+				unsigned int type,
+				unsigned long *fs_pages_to_unuse)
 {
 	pmd_t *pmd;
 	unsigned long next;
@@ -1231,8 +1243,9 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
 		next = pmd_addr_end(addr, end);
 		if (pmd_none_or_trans_huge_or_clear_bad(pmd))
 			continue;
-		ret = unuse_pte_range(vma, pmd, addr, next, type);
-		if (ret < 0)
+		ret = unuse_pte_range(vma, pmd, addr, next, type,
+				fs_pages_to_unuse);
+		if (ret < 0 || ret == 2)
 			return ret;
 	} while (pmd++, addr = next, addr != end);
 	return 0;
@@ -1240,7 +1253,8 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
 
 static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
 				unsigned long addr, unsigned long end,
-				unsigned int type)
+				unsigned int type,
+				unsigned long *fs_pages_to_unuse)
 {
 	pud_t *pud;
 	unsigned long next;
@@ -1251,14 +1265,16 @@ static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
 		next = pud_addr_end(addr, end);
 		if (pud_none_or_clear_bad(pud))
 			continue;
-		ret = unuse_pmd_range(vma, pud, addr, next, type);
-		if (ret < 0)
+		ret = unuse_pmd_range(vma, pud, addr, next, type,
+				fs_pages_to_unuse);
+		if (ret < 0 || ret == 2)
 			return ret;
 	} while (pud++, addr = next, addr != end);
 	return 0;
 }
 
-static int unuse_vma(struct vm_area_struct *vma, unsigned int type)
+static int unuse_vma(struct vm_area_struct *vma, unsigned int type,
+		unsigned long *fs_pages_to_unuse)
 {
 	pgd_t *pgd;
 	unsigned long addr, end, next;
@@ -1272,14 +1288,16 @@ static int unuse_vma(struct vm_area_struct *vma, unsigned int type)
 		next = pgd_addr_end(addr, end);
 		if (pgd_none_or_clear_bad(pgd))
 			continue;
-		ret = unuse_pud_range(vma, pgd, addr, next, type);
-		if (ret < 0)
+		ret = unuse_pud_range(vma, pgd, addr, next, type,
+				fs_pages_to_unuse);
+		if (ret < 0 || ret == 2)
 			return ret;
 	} while (pgd++, addr = next, addr != end);
 	return 0;
 }
 
-static int unuse_mm(struct mm_struct *mm, unsigned int type)
+static int unuse_mm(struct mm_struct *mm, unsigned int type,
+		unsigned long *fs_pages_to_unuse)
 {
 	struct vm_area_struct *vma;
 	int ret = 0;
@@ -1287,7 +1305,7 @@ static int unuse_mm(struct mm_struct *mm, unsigned int type)
 	down_read(&mm->mmap_sem);
 	for (vma = mm->mmap; vma; vma = vma->vm_next) {
 		if (vma->anon_vma) {
-			ret = unuse_vma(vma, type);
+			ret = unuse_vma(vma, type, fs_pages_to_unuse);
 			if (ret)
 				break;
 		}
@@ -1342,7 +1360,6 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si,
 	return i;
 }
 
-/* TODO: frontswap */
 #define MAX_RETRIES 3
 int try_to_unuse(unsigned int type, bool frontswap,
 		 unsigned long pages_to_unuse)
@@ -1354,10 +1371,14 @@ int try_to_unuse(unsigned int type, bool frontswap,
 	struct swap_info_struct *si = swap_info[type];
 	struct page *page;
 	swp_entry_t entry;
+	unsigned long fs_pages_to_unuse = 0;
 	unsigned int i = 0;
 	unsigned int oldi = 0;
 	int retries = 0;
 
+	if (frontswap)
+		fs_pages_to_unuse = pages_to_unuse;
+
 retry:
 	retval = shmem_unuse(type);
 	if (retval)
@@ -1381,9 +1402,7 @@ retry:
 		mmput(prev_mm);
 		prev_mm = mm;
 
-		retval = unuse_mm(mm, type);
-		if (retval)
-			goto out_put;
+		retval = unuse_mm(mm, type, &fs_pages_to_unuse);
 
 		/*
 		 * Make sure that we aren't completely killing
@@ -1396,8 +1415,13 @@ retry:
 
 out_put:
 	mmput(prev_mm);
-	if (retval)
+	if (retval < 0)
+		goto out;
+	if (retval == FRONTSWAP_PAGES_UNUSED) {
+		retval = 0;
 		goto out;
+	}
+
 	while ((i = find_next_to_unuse(si, i, frontswap)) != 0) {
 		/*
 		 * under global memory pressure, swap entries
@@ -1410,7 +1434,7 @@ out_put:
 		 */
 		if (i < oldi) {
 			retries++;
-			if (retries > MAX_RETRIES) {
+			if ((retries > MAX_RETRIES) || frontswap) {
 				retval = -EBUSY;
 				goto out;
 			}
Signature · 7 lines
1.8.3.2

--
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>
maintainer
#1 lore raw
Quoted 5 lines · On Thu, 19 Mar 2015 03:55:45 -0700 Kelley Nielsen <[email protected]> wrote:

The prototype of the new swapoff (without the quadratic complexity) presently ignores the frontswap case. Pass the count of pages_to_unuse down the page table walks in try_to_unuse(), and return from the walk when the desired number of pages has been swapped back in.

Does this fix the "TODO" in [1/2]?

Do you think this patchset is ready for testing (while Hugh reviews it :)), or is there some deeper reason behind the "RFC"?

--
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>

Acked-by
#2 lore raw
Quoted 8 lines · Andrew Morton
Quoted 5 lines · On Thu, 19 Mar 2015 03:55:45 -0700 Kelley Nielsen <[email protected]> wrote:

The prototype of the new swapoff (without the quadratic complexity) presently ignores the frontswap case. Pass the count of pages_to_unuse down the page table walks in try_to_unuse(), and return from the walk when the desired number of pages has been swapped back in.

Quoted 4 lines

Does this fix the "TODO" in [1/2]?

Do you think this patchset is ready for testing (while Hugh reviews it :)), or is there some deeper reason behind the "RFC"?

The patches look good to me, and this seems to
address the TODO from patch 1/2.

Kelley put in a heroic amount of effort tracking down and fixing the corner cases I failed to anticipate before putting this up as an OPW internship project.

--
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
In reply to Kelley Nielsen
Quoted 11 lines · Kelley Nielsen

The prototype of the new swapoff (without the quadratic complexity) presently ignores the frontswap case. Pass the count of pages_to_unuse down the page table walks in try_to_unuse(), and return from the walk when the desired number of pages has been swapped back in.

---
Changes since v6:

Quoted 11 lines

- In try_to_unuse(), copy pages_to_unuse to local fs_pages_to_unuse and pass by reference (instead of value) down the page table walk
- create #define FRONTSWAP_PAGES_UNUSED to pass special code back up the chain from unuse_pte_range()
- Test for this return code in try_to_unuse() and exit before orphans cleanup and retries
- In unuse_pte_range, move decrement of frontswap pages count next to call to delete_from_swap_cache(), so that it happens on every unused swap slot instead of every pte unmap
- Remove unnecessary goto after call to unuse_mm() in try_to_unuse()
- Remove blank line in shmem.c/shmem_unuse_inode()

Some of those seem just cleanups to fold into the previous patch.

I'd be okay if you just folded the two patches together, but if you prefer to handle the frontswap partial case in this separate patch, that's fine.

Quoted 3 lines
---
 mm/swapfile.c | 60 +++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 42 insertions(+), 18 deletions(-)

This is incomplete: why no change to shmem_unuse_inode()? You're often unable to satisfy frontswap's demand without that.

mm/swapfile.c out_nolock: · line 1147

Quoted 9 lines
mm/swapfile.c +1 -0
index 79c47b6..e4f5289 100644
@@ -1144,9 +1144,11 @@ out_nolock:
 	return ret;
 }
 
+#define FRONTSWAP_PAGES_UNUSED 2

Okay, that'll work. But I wonder why you chose 2 rather than the more obvious 1 - coupled with the name, it makes it sound like a number of pages.

(Personally I'd have gone for a different approach, returning the negative error, or the number of pages still to be unused: that has the benefit that you get out of long scans sooner, even in the non-frontswap case: once si->inuse_pages falls to 0, there's no more work to be done. Or perhaps you tried that way, but it doesn't work out - might need exporting info from swapfile.c to shmem.c.)

Quoted 9 lines
mm/swapfile.c +2 -1
 static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 				unsigned long addr, unsigned long end,
-				unsigned int type)
+				unsigned int type,
+				unsigned long *fs_pages_to_unuse)
 {
 	struct page *page;
 	swp_entry_t entry;

mm/swapfile.c static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd, · line 1175

Quoted 5 lines
mm/swapfile.c +2 -0
@@ -1169,6 +1171,8 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 			continue;
 		if (found_type != type)
 			continue;
+		if ((*fs_pages_to_unuse > 0) && (!frontswap_test(si, offset)))
+				continue;

There's a tab too many on the line above.

Quoted 32 lines
mm/swapfile.c +9 -1
 		swap_map = &si->swap_map[offset];
 		if (!swap_count(*swap_map))
@@ -1205,11 +1209,18 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 		if (PageSwapCache(page) && (swap_count(*swap_map) == 0)) {
 			wait_on_page_writeback(page);
 			delete_from_swap_cache(page);
+			if (*fs_pages_to_unuse > 0) {
+				(*fs_pages_to_unuse)--;
+				if (*fs_pages_to_unuse == 0)
+					ret = FRONTSWAP_PAGES_UNUSED;
+			}
 		}
 
 		SetPageDirty(page);
 		unlock_page(page);
 		page_cache_release(page);
+		if (ret == FRONTSWAP_PAGES_UNUSED)
+			goto out;
 try_next:
 		pte = pte_offset_map(pmd, addr);
 	} while (pte++, addr += PAGE_SIZE, addr != end);
@@ -1220,7 +1231,8 @@ out:
 
 static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
 				unsigned long addr, unsigned long end,
-				unsigned int type)
+				unsigned int type,
+				unsigned long *fs_pages_to_unuse)
 {
 	pmd_t *pmd;
 	unsigned long next;

mm/swapfile.c static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud, · line 1248

Quoted 8 lines
mm/swapfile.c +3 -2
@@ -1231,8 +1243,9 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
 		next = pmd_addr_end(addr, end);
 		if (pmd_none_or_trans_huge_or_clear_bad(pmd))
 			continue;
-		ret = unuse_pte_range(vma, pmd, addr, next, type);
-		if (ret < 0)
+		ret = unuse_pte_range(vma, pmd, addr, next, type,
+				fs_pages_to_unuse);
+		if (ret < 0 || ret == 2)

FRONTSWAP_PAGES_UNUSED there.

Quoted 14 lines
mm/swapfile.c +2 -1
 			return ret;
 	} while (pmd++, addr = next, addr != end);
 	return 0;
@@ -1240,7 +1253,8 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
 
 static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
 				unsigned long addr, unsigned long end,
-				unsigned int type)
+				unsigned int type,
+				unsigned long *fs_pages_to_unuse)
 {
 	pud_t *pud;
 	unsigned long next;

mm/swapfile.c static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd, · line 1270

Quoted 8 lines
mm/swapfile.c +3 -2
@@ -1251,14 +1265,16 @@ static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
 		next = pud_addr_end(addr, end);
 		if (pud_none_or_clear_bad(pud))
 			continue;
-		ret = unuse_pmd_range(vma, pud, addr, next, type);
-		if (ret < 0)
+		ret = unuse_pmd_range(vma, pud, addr, next, type,
+				fs_pages_to_unuse);
+		if (ret < 0 || ret == 2)

FRONTSWAP_PAGES_UNUSED there.

Quoted 12 lines
mm/swapfile.c +2 -1
 			return ret;
 	} while (pud++, addr = next, addr != end);
 	return 0;
 }
 
-static int unuse_vma(struct vm_area_struct *vma, unsigned int type)
+static int unuse_vma(struct vm_area_struct *vma, unsigned int type,
+		unsigned long *fs_pages_to_unuse)
 {
 	pgd_t *pgd;
 	unsigned long addr, end, next;

mm/swapfile.c static int unuse_vma(struct vm_area_struct *vma, unsigned int type) · line 1293

Quoted 8 lines
mm/swapfile.c +3 -2
@@ -1272,14 +1288,16 @@ static int unuse_vma(struct vm_area_struct *vma, unsigned int type)
 		next = pgd_addr_end(addr, end);
 		if (pgd_none_or_clear_bad(pgd))
 			continue;
-		ret = unuse_pud_range(vma, pgd, addr, next, type);
-		if (ret < 0)
+		ret = unuse_pud_range(vma, pgd, addr, next, type,
+				fs_pages_to_unuse);
+		if (ret < 0 || ret == 2)

FRONTSWAP_PAGES_UNUSED there.

Quoted 30 lines
mm/swapfile.c +3 -3
 			return ret;
 	} while (pgd++, addr = next, addr != end);
 	return 0;
 }
 
-static int unuse_mm(struct mm_struct *mm, unsigned int type)
+static int unuse_mm(struct mm_struct *mm, unsigned int type,
+		unsigned long *fs_pages_to_unuse)
 {
 	struct vm_area_struct *vma;
 	int ret = 0;
@@ -1287,7 +1305,7 @@ static int unuse_mm(struct mm_struct *mm, unsigned int type)
 	down_read(&mm->mmap_sem);
 	for (vma = mm->mmap; vma; vma = vma->vm_next) {
 		if (vma->anon_vma) {
-			ret = unuse_vma(vma, type);
+			ret = unuse_vma(vma, type, fs_pages_to_unuse);
 			if (ret)
 				break;
 		}
@@ -1342,7 +1360,6 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si,
 	return i;
 }
 
-/* TODO: frontswap */
 #define MAX_RETRIES 3
 int try_to_unuse(unsigned int type, bool frontswap,
 		 unsigned long pages_to_unuse)
@@ -1354,10 +1371,14 @@ int try_to_unuse(unsigned int type, bool frontswap,
 	struct swap_info_struct *si = swap_info[type];

mm/swapfile.c int try_to_unuse(unsigned int type, bool frontswap, · line 1381

Quoted 10 lines
mm/swapfile.c +4 -0
 	struct page *page;
 	swp_entry_t entry;
+	unsigned long fs_pages_to_unuse = 0;
 	unsigned int i = 0;
 	unsigned int oldi = 0;
 	int retries = 0;
 
+	if (frontswap)
+		fs_pages_to_unuse = pages_to_unuse;
+

If you wish. But I can't see why you don't just use pages_to_unuse throughout. (That "bool frontswap" in the interface was always silly.)

Quoted 30 lines
mm/swapfile.c +7 -4
 retry:
 	retval = shmem_unuse(type);
 	if (retval)
@@ -1381,9 +1402,7 @@ retry:
 		mmput(prev_mm);
 		prev_mm = mm;
 
-		retval = unuse_mm(mm, type);
-		if (retval)
-			goto out_put;
+		retval = unuse_mm(mm, type, &fs_pages_to_unuse);
 
 		/*
 		 * Make sure that we aren't completely killing
@@ -1396,8 +1415,13 @@ retry:
 
 out_put:
 	mmput(prev_mm);
-	if (retval)
+	if (retval < 0)
+		goto out;
+	if (retval == FRONTSWAP_PAGES_UNUSED) {
+		retval = 0;
 		goto out;
+	}
+
 	while ((i = find_next_to_unuse(si, i, frontswap)) != 0) {
 		/*
 		 * under global memory pressure, swap entries

mm/swapfile.c out_put: · line 1437

Quoted 5 lines
mm/swapfile.c +1 -1
@@ -1410,7 +1434,7 @@ out_put:
 		 */
 		if (i < oldi) {
 			retries++;
-			if (retries > MAX_RETRIES) {
+			if ((retries > MAX_RETRIES) || frontswap) {

I don't understand why you omit retrying in the frontswap case?

Quoted 5 lines
mm/swapfile.c +0 -0
 				retval = -EBUSY;
 				goto out;
 			}
Signature · 1 line
1.8.3.2

--
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>