Skip to content

Commit

Permalink
tmpfs: make shmem_unuse more preemptible
Browse files Browse the repository at this point in the history
shmem_unuse is at present an unbroken search through every swap vector page of
every tmpfs file which might be swapped, all under shmem_swaplist_lock.  This
dates from long ago, when the caller held mmlist_lock over it all too: long
gone, but there's never been much pressure for preemptible swapoff.

Make it a little more preemptible, replacing shmem_swaplist_lock by
shmem_swaplist_mutex, inserting a cond_resched in the main loop, and a
cond_resched_lock (on info->lock) at one convenient point in the
shmem_unuse_inode loop, where it has no outstanding kmap_atomic.

If we're serious about preemptible swapoff, there's much further to go e.g.
I'm stupid to let the kmap_atomics of the decreasingly significant HIGHMEM
case dictate preemptiblility for other configs.  But as in the earlier patch
to make swapoff scan ptes preemptibly, my hidden agenda is really towards
making memcgroups work, hardly about preemptibility at all.

Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Hugh Dickins authored and Linus Torvalds committed Feb 5, 2008
1 parent a0ee5ec commit cb5f7b9
Showing 1 changed file with 16 additions and 7 deletions.
23 changes: 16 additions & 7 deletions mm/shmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ static struct backing_dev_info shmem_backing_dev_info __read_mostly = {
};

static LIST_HEAD(shmem_swaplist);
static DEFINE_SPINLOCK(shmem_swaplist_lock);
static DEFINE_MUTEX(shmem_swaplist_mutex);

static void shmem_free_blocks(struct inode *inode, long pages)
{
Expand Down Expand Up @@ -796,9 +796,9 @@ static void shmem_delete_inode(struct inode *inode)
inode->i_size = 0;
shmem_truncate(inode);
if (!list_empty(&info->swaplist)) {
spin_lock(&shmem_swaplist_lock);
mutex_lock(&shmem_swaplist_mutex);
list_del_init(&info->swaplist);
spin_unlock(&shmem_swaplist_lock);
mutex_unlock(&shmem_swaplist_mutex);
}
}
BUG_ON(inode->i_blocks);
Expand Down Expand Up @@ -851,6 +851,14 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s
for (idx = SHMEM_NR_DIRECT; idx < limit; idx += ENTRIES_PER_PAGE, dir++) {
if (unlikely(idx == stage)) {
shmem_dir_unmap(dir-1);
if (cond_resched_lock(&info->lock)) {
/* check it has not been truncated */
if (limit > info->next_index) {
limit = info->next_index;
if (idx >= limit)
goto lost2;
}
}
dir = shmem_dir_map(info->i_indirect) +
ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
while (!*dir) {
Expand Down Expand Up @@ -924,7 +932,7 @@ int shmem_unuse(swp_entry_t entry, struct page *page)
struct shmem_inode_info *info;
int found = 0;

spin_lock(&shmem_swaplist_lock);
mutex_lock(&shmem_swaplist_mutex);
list_for_each_safe(p, next, &shmem_swaplist) {
info = list_entry(p, struct shmem_inode_info, swaplist);
if (!info->swapped)
Expand All @@ -935,8 +943,9 @@ int shmem_unuse(swp_entry_t entry, struct page *page)
found = 1;
break;
}
cond_resched();
}
spin_unlock(&shmem_swaplist_lock);
mutex_unlock(&shmem_swaplist_mutex);
return found;
}

Expand Down Expand Up @@ -996,10 +1005,10 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
shmem_swp_unmap(entry);
spin_unlock(&info->lock);
if (list_empty(&info->swaplist)) {
spin_lock(&shmem_swaplist_lock);
mutex_lock(&shmem_swaplist_mutex);
/* move instead of add in case we're racing */
list_move_tail(&info->swaplist, &shmem_swaplist);
spin_unlock(&shmem_swaplist_lock);
mutex_unlock(&shmem_swaplist_mutex);
}
swap_duplicate(swap);
BUG_ON(page_mapped(page));
Expand Down

0 comments on commit cb5f7b9

Please sign in to comment.