[glsdk/meta-ti-glsdk.git] / recipes-kernel / linux / linux-ti33x-psp-3.2 / 3.2.2 / 0128-SHM_UNLOCK-fix-Unevictable-pages-stranded-after-swap.patch
1 From 67890984571f0de5f3d975ef1d67643d89d92174 Mon Sep 17 00:00:00 2001
2 From: Hugh Dickins <hughd@google.com>
3 Date: Fri, 20 Jan 2012 14:34:21 -0800
4 Subject: [PATCH 128/129] SHM_UNLOCK: fix Unevictable pages stranded after
5 swap
7 commit 245132643e1cfcd145bbc86a716c1818371fcb93 upstream.
9 Commit cc39c6a9bbde ("mm: account skipped entries to avoid looping in
10 find_get_pages") correctly fixed an infinite loop; but left a problem
11 that find_get_pages() on shmem would return 0 (appearing to callers to
12 mean end of tree) when it meets a run of nr_pages swap entries.
14 The only uses of find_get_pages() on shmem are via pagevec_lookup(),
15 called from invalidate_mapping_pages(), and from shmctl SHM_UNLOCK's
16 scan_mapping_unevictable_pages(). The first is already commented, and
17 not worth worrying about; but the second can leave pages on the
18 Unevictable list after an unusual sequence of swapping and locking.
20 Fix that by using shmem_find_get_pages_and_swap() (then ignoring the
21 swap) instead of pagevec_lookup().
23 But I don't want to contaminate vmscan.c with shmem internals, nor
24 shmem.c with LRU locking. So move scan_mapping_unevictable_pages() into
25 shmem.c, renaming it shmem_unlock_mapping(); and rename
26 check_move_unevictable_page() to check_move_unevictable_pages(), looping
27 down an array of pages, oftentimes under the same lock.
29 Leave out the "rotate unevictable list" block: that's a leftover from
30 when this was used for /proc/sys/vm/scan_unevictable_pages, whose flawed
31 handling involved looking at pages at tail of LRU.
33 Was there significance to the sequence first ClearPageUnevictable, then
34 test page_evictable, then SetPageUnevictable here? I think not, we're
35 under LRU lock, and have no barriers between those.
37 Signed-off-by: Hugh Dickins <hughd@google.com>
38 Reviewed-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
39 Cc: Minchan Kim <minchan.kim@gmail.com>
40 Cc: Rik van Riel <riel@redhat.com>
41 Cc: Shaohua Li <shaohua.li@intel.com>
42 Cc: Eric Dumazet <eric.dumazet@gmail.com>
43 Cc: Johannes Weiner <hannes@cmpxchg.org>
44 Cc: Michel Lespinasse <walken@google.com>
45 Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
46 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
47 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
48 ---
49 include/linux/shmem_fs.h | 1 +
50 include/linux/swap.h | 2 +-
51 ipc/shm.c | 2 +-
52 mm/shmem.c | 46 +++++++++++++++--
53 mm/vmscan.c | 122 ++++++++++++++--------------------------------
54 5 files changed, 81 insertions(+), 92 deletions(-)
56 diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
57 index 9291ac3..6f10c9c 100644
58 --- a/include/linux/shmem_fs.h
59 +++ b/include/linux/shmem_fs.h
60 @@ -48,6 +48,7 @@ extern struct file *shmem_file_setup(const char *name,
61 loff_t size, unsigned long flags);
62 extern int shmem_zero_setup(struct vm_area_struct *);
63 extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
64 +extern void shmem_unlock_mapping(struct address_space *mapping);
65 extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
66 pgoff_t index, gfp_t gfp_mask);
67 extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end);
68 diff --git a/include/linux/swap.h b/include/linux/swap.h
69 index 1e22e12..67b3fa3 100644
70 --- a/include/linux/swap.h
71 +++ b/include/linux/swap.h
72 @@ -272,7 +272,7 @@ static inline int zone_reclaim(struct zone *z, gfp_t mask, unsigned int order)
73 #endif
75 extern int page_evictable(struct page *page, struct vm_area_struct *vma);
76 -extern void scan_mapping_unevictable_pages(struct address_space *);
77 +extern void check_move_unevictable_pages(struct page **, int nr_pages);
79 extern unsigned long scan_unevictable_pages;
80 extern int scan_unevictable_handler(struct ctl_table *, int,
81 diff --git a/ipc/shm.c b/ipc/shm.c
82 index 854ab58..b76be5b 100644
83 --- a/ipc/shm.c
84 +++ b/ipc/shm.c
85 @@ -916,7 +916,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
86 shp->mlock_user = NULL;
87 get_file(shm_file);
88 shm_unlock(shp);
89 - scan_mapping_unevictable_pages(shm_file->f_mapping);
90 + shmem_unlock_mapping(shm_file->f_mapping);
91 fput(shm_file);
92 goto out;
93 }
94 diff --git a/mm/shmem.c b/mm/shmem.c
95 index cc6d40b2..6c253f7 100644
96 --- a/mm/shmem.c
97 +++ b/mm/shmem.c
98 @@ -379,7 +379,7 @@ static int shmem_free_swap(struct address_space *mapping,
99 /*
100 * Pagevec may contain swap entries, so shuffle up pages before releasing.
101 */
102 -static void shmem_pagevec_release(struct pagevec *pvec)
103 +static void shmem_deswap_pagevec(struct pagevec *pvec)
104 {
105 int i, j;
107 @@ -389,7 +389,36 @@ static void shmem_pagevec_release(struct pagevec *pvec)
108 pvec->pages[j++] = page;
109 }
110 pvec->nr = j;
111 - pagevec_release(pvec);
112 +}
113 +
114 +/*
115 + * SysV IPC SHM_UNLOCK restore Unevictable pages to their evictable lists.
116 + */
117 +void shmem_unlock_mapping(struct address_space *mapping)
118 +{
119 + struct pagevec pvec;
120 + pgoff_t indices[PAGEVEC_SIZE];
121 + pgoff_t index = 0;
122 +
123 + pagevec_init(&pvec, 0);
124 + /*
125 + * Minor point, but we might as well stop if someone else SHM_LOCKs it.
126 + */
127 + while (!mapping_unevictable(mapping)) {
128 + /*
129 + * Avoid pagevec_lookup(): find_get_pages() returns 0 as if it
130 + * has finished, if it hits a row of PAGEVEC_SIZE swap entries.
131 + */
132 + pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
133 + PAGEVEC_SIZE, pvec.pages, indices);
134 + if (!pvec.nr)
135 + break;
136 + index = indices[pvec.nr - 1] + 1;
137 + shmem_deswap_pagevec(&pvec);
138 + check_move_unevictable_pages(pvec.pages, pvec.nr);
139 + pagevec_release(&pvec);
140 + cond_resched();
141 + }
142 }
144 /*
145 @@ -440,7 +469,8 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
146 }
147 unlock_page(page);
148 }
149 - shmem_pagevec_release(&pvec);
150 + shmem_deswap_pagevec(&pvec);
151 + pagevec_release(&pvec);
152 mem_cgroup_uncharge_end();
153 cond_resched();
154 index++;
155 @@ -470,7 +500,8 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
156 continue;
157 }
158 if (index == start && indices[0] > end) {
159 - shmem_pagevec_release(&pvec);
160 + shmem_deswap_pagevec(&pvec);
161 + pagevec_release(&pvec);
162 break;
163 }
164 mem_cgroup_uncharge_start();
165 @@ -494,7 +525,8 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
166 }
167 unlock_page(page);
168 }
169 - shmem_pagevec_release(&pvec);
170 + shmem_deswap_pagevec(&pvec);
171 + pagevec_release(&pvec);
172 mem_cgroup_uncharge_end();
173 index++;
174 }
175 @@ -2439,6 +2471,10 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user)
176 return 0;
177 }
179 +void shmem_unlock_mapping(struct address_space *mapping)
180 +{
181 +}
182 +
183 void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
184 {
185 truncate_inode_pages_range(inode->i_mapping, lstart, lend);
186 diff --git a/mm/vmscan.c b/mm/vmscan.c
187 index 824676a..cb33d9c 100644
188 --- a/mm/vmscan.c
189 +++ b/mm/vmscan.c
190 @@ -636,7 +636,7 @@ redo:
191 * When racing with an mlock or AS_UNEVICTABLE clearing
192 * (page is unlocked) make sure that if the other thread
193 * does not observe our setting of PG_lru and fails
194 - * isolation/check_move_unevictable_page,
195 + * isolation/check_move_unevictable_pages,
196 * we see PG_mlocked/AS_UNEVICTABLE cleared below and move
197 * the page back to the evictable list.
198 *
199 @@ -3355,104 +3355,56 @@ int page_evictable(struct page *page, struct vm_area_struct *vma)
201 #ifdef CONFIG_SHMEM
202 /**
203 - * check_move_unevictable_page - check page for evictability and move to appropriate zone lru list
204 - * @page: page to check evictability and move to appropriate lru list
205 - * @zone: zone page is in
206 + * check_move_unevictable_pages - check pages for evictability and move to appropriate zone lru list
207 + * @pages: array of pages to check
208 + * @nr_pages: number of pages to check
209 *
210 - * Checks a page for evictability and moves the page to the appropriate
211 - * zone lru list.
212 - *
213 - * Restrictions: zone->lru_lock must be held, page must be on LRU and must
214 - * have PageUnevictable set.
215 + * Checks pages for evictability and moves them to the appropriate lru list.
216 *
217 * This function is only used for SysV IPC SHM_UNLOCK.
218 */
219 -static void check_move_unevictable_page(struct page *page, struct zone *zone)
220 +void check_move_unevictable_pages(struct page **pages, int nr_pages)
221 {
222 - VM_BUG_ON(PageActive(page));
223 + struct zone *zone = NULL;
224 + int pgscanned = 0;
225 + int pgrescued = 0;
226 + int i;
228 -retry:
229 - ClearPageUnevictable(page);
230 - if (page_evictable(page, NULL)) {
231 - enum lru_list l = page_lru_base_type(page);
232 + for (i = 0; i < nr_pages; i++) {
233 + struct page *page = pages[i];
234 + struct zone *pagezone;
236 - __dec_zone_state(zone, NR_UNEVICTABLE);
237 - list_move(&page->lru, &zone->lru[l].list);
238 - mem_cgroup_move_lists(page, LRU_UNEVICTABLE, l);
239 - __inc_zone_state(zone, NR_INACTIVE_ANON + l);
240 - __count_vm_event(UNEVICTABLE_PGRESCUED);
241 - } else {
242 - /*
243 - * rotate unevictable list
244 - */
245 - SetPageUnevictable(page);
246 - list_move(&page->lru, &zone->lru[LRU_UNEVICTABLE].list);
247 - mem_cgroup_rotate_lru_list(page, LRU_UNEVICTABLE);
248 - if (page_evictable(page, NULL))
249 - goto retry;
250 - }
251 -}
252 -
253 -/**
254 - * scan_mapping_unevictable_pages - scan an address space for evictable pages
255 - * @mapping: struct address_space to scan for evictable pages
256 - *
257 - * Scan all pages in mapping. Check unevictable pages for
258 - * evictability and move them to the appropriate zone lru list.
259 - *
260 - * This function is only used for SysV IPC SHM_UNLOCK.
261 - */
262 -void scan_mapping_unevictable_pages(struct address_space *mapping)
263 -{
264 - pgoff_t next = 0;
265 - pgoff_t end = (i_size_read(mapping->host) + PAGE_CACHE_SIZE - 1) >>
266 - PAGE_CACHE_SHIFT;
267 - struct zone *zone;
268 - struct pagevec pvec;
269 + pgscanned++;
270 + pagezone = page_zone(page);
271 + if (pagezone != zone) {
272 + if (zone)
273 + spin_unlock_irq(&zone->lru_lock);
274 + zone = pagezone;
275 + spin_lock_irq(&zone->lru_lock);
276 + }
278 - if (mapping->nrpages == 0)
279 - return;
280 + if (!PageLRU(page) || !PageUnevictable(page))
281 + continue;
283 - pagevec_init(&pvec, 0);
284 - while (next < end &&
285 - pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
286 - int i;
287 - int pg_scanned = 0;
288 -
289 - zone = NULL;
290 -
291 - for (i = 0; i < pagevec_count(&pvec); i++) {
292 - struct page *page = pvec.pages[i];
293 - pgoff_t page_index = page->index;
294 - struct zone *pagezone = page_zone(page);
295 -
296 - pg_scanned++;
297 - if (page_index > next)
298 - next = page_index;
299 - next++;
300 -
301 - if (pagezone != zone) {
302 - if (zone)
303 - spin_unlock_irq(&zone->lru_lock);
304 - zone = pagezone;
305 - spin_lock_irq(&zone->lru_lock);
306 - }
307 + if (page_evictable(page, NULL)) {
308 + enum lru_list lru = page_lru_base_type(page);
310 - if (PageLRU(page) && PageUnevictable(page))
311 - check_move_unevictable_page(page, zone);
312 + VM_BUG_ON(PageActive(page));
313 + ClearPageUnevictable(page);
314 + __dec_zone_state(zone, NR_UNEVICTABLE);
315 + list_move(&page->lru, &zone->lru[lru].list);
316 + mem_cgroup_move_lists(page, LRU_UNEVICTABLE, lru);
317 + __inc_zone_state(zone, NR_INACTIVE_ANON + lru);
318 + pgrescued++;
319 }
320 - if (zone)
321 - spin_unlock_irq(&zone->lru_lock);
322 - pagevec_release(&pvec);
323 + }
325 - count_vm_events(UNEVICTABLE_PGSCANNED, pg_scanned);
326 - cond_resched();
327 + if (zone) {
328 + __count_vm_events(UNEVICTABLE_PGRESCUED, pgrescued);
329 + __count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned);
330 + spin_unlock_irq(&zone->lru_lock);
331 }
332 }
333 -#else
334 -void scan_mapping_unevictable_pages(struct address_space *mapping)
335 -{
336 -}
337 #endif /* CONFIG_SHMEM */
339 static void warn_scan_unevictable_pages(void)
340 --
341 1.7.9.5