Messages in this thread Patch in this message | | | Date | Sat, 01 Jun 2002 01:44:03 -0700 | From | Andrew Morton <> | Subject | [patch 14/16] tmpfs bugfixes |
| |
A patch from Hugh Dickins which fixes a couple of error-path leaks related to tmpfs (I think).
Also fixes a yield()-inside-spinlock bug.
It also includes code to clear the final page outside i_size on truncate. tmpfs should be returning zeroes when a truncated file is later expanded and it currently is not.
Hugh is taking care of the 2.4 fix for this.
=====================================
--- 2.5.19/mm/shmem.c~hugh Sat Jun 1 01:18:13 2002 +++ 2.5.19-akpm/mm/shmem.c Sat Jun 1 01:18:13 2002 @@ -52,6 +52,8 @@ LIST_HEAD (shmem_inodes); static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED; atomic_t shmem_nrpages = ATOMIC_INIT(0); /* Not used right now */ +static struct page *shmem_getpage_locked(struct shmem_inode_info *, struct inode *, unsigned long); + #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512) /* @@ -317,6 +319,7 @@ shmem_truncate_indirect(struct shmem_ino static void shmem_truncate (struct inode * inode) { unsigned long index; + unsigned long partial; unsigned long freed = 0; struct shmem_inode_info * info = SHMEM_I(inode); @@ -324,6 +327,28 @@ static void shmem_truncate (struct inode inode->i_ctime = inode->i_mtime = CURRENT_TIME; spin_lock (&info->lock); index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + partial = inode->i_size & ~PAGE_CACHE_MASK; + + if (partial) { + swp_entry_t *entry = shmem_swp_entry(info, index-1, 0); + struct page *page; + /* + * This check is racy: it's faintly possible that page + * was assigned to swap during truncate_inode_pages, + * and now assigned to file; but better than nothing. + */ + if (!IS_ERR(entry) && entry->val) { + spin_unlock(&info->lock); + page = shmem_getpage_locked(info, inode, index-1); + if (!IS_ERR(page)) { + memclear_highpage_flush(page, partial, + PAGE_CACHE_SIZE - partial); + unlock_page(page); + page_cache_release(page); + } + spin_lock(&info->lock); + } + } while (index < info->next_index) freed += shmem_truncate_indirect(info, index); @@ -369,10 +394,10 @@ static int shmem_unuse_inode (struct shm swp_entry_t *ptr; unsigned long idx; int offset; + struct inode *inode = NULL; - spin_lock (&info->lock); -repeat: idx = 0; + spin_lock (&info->lock); offset = shmem_clear_swp (entry, info->i_direct, SHMEM_NR_DIRECT); if (offset >= 0) goto found; @@ -389,20 +414,36 @@ repeat: spin_unlock (&info->lock); return 0; found: - if (!move_from_swap_cache (page, offset+idx, info->vfs_inode.i_mapping)) { - info->swapped--; - SetPageUptodate (page); - spin_unlock (&info->lock); - return 1; + idx += offset; + inode = igrab(&info->vfs_inode); + spin_unlock(&shmem_ilock); + + while (inode && move_from_swap_cache(page, idx, inode->i_mapping)) { + /* + * Yield for kswapd, and try again - but we're still + * holding the page lock - ugh! fix this up later on. + * Beware of inode being unlinked or truncated: just + * leave try_to_unuse to delete_from_swap_cache if so. + */ + spin_unlock(&info->lock); + yield(); + spin_lock(&info->lock); + ptr = shmem_swp_entry(info, idx, 0); + if (IS_ERR(ptr)) + break; } - /* Yield for kswapd, and try again */ - yield(); - goto repeat; + info->swapped--; + SetPageUptodate(page); + spin_unlock(&info->lock); + if (inode) + iput(inode); + return 1; } /* - * unuse_shmem() search for an eventually swapped out shmem page. + * shmem_unuse() search for an eventually swapped out shmem page. + * Note shmem_unuse_inode drops shmem_ilock itself if successful. */ void shmem_unuse(swp_entry_t entry, struct page *page) { @@ -414,7 +455,7 @@ void shmem_unuse(swp_entry_t entry, stru info = list_entry(p, struct shmem_inode_info, list); if (shmem_unuse_inode(info, entry, page)) - break; + return; } spin_unlock (&shmem_ilock); } @@ -551,6 +592,7 @@ repeat: error = move_from_swap_cache(page, idx, mapping); if (error < 0) { unlock_page(page); + page_cache_release(page); return ERR_PTR(error); } @@ -576,11 +618,11 @@ repeat: * is enough to make this atomic. */ page = page_cache_alloc(mapping); if (!page) - return ERR_PTR(-ENOMEM); + goto no_mem; error = add_to_page_cache(page, mapping, idx); if (error < 0) { page_cache_release(page); - return ERR_PTR(-ENOMEM); + goto no_mem; } clear_highpage(page); inode->i_blocks += BLOCKS_PER_PAGE; @@ -589,6 +631,13 @@ repeat: /* We have the page */ SetPageUptodate(page); return page; + +no_mem: + spin_lock(&sbinfo->stat_lock); + sbinfo->free_blocks++; + spin_unlock(&sbinfo->stat_lock); + return ERR_PTR(-ENOMEM); + no_space: spin_unlock (&sbinfo->stat_lock); return ERR_PTR(-ENOSPC); @@ -1396,6 +1445,7 @@ static void destroy_inodecache(void) static struct address_space_operations shmem_aops = { writepage: shmem_writepage, + set_page_dirty: __set_page_dirty_nobuffers, }; static struct file_operations shmem_file_operations = { - - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |