⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shmem.c

📁 最新最稳定的Linux内存管理模块源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
			ptr = shmem_swp_map(subdir);		}	}	shmem_swp_unmap(ptr);	return freed;}static void shmem_free_pages(struct list_head *next){	struct page *page;	int freed = 0;	do {		page = container_of(next, struct page, lru);		next = next->next;		shmem_dir_free(page);		freed++;		if (freed >= LATENCY_LIMIT) {			cond_resched();			freed = 0;		}	} while (next);}static void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end){	struct shmem_inode_info *info = SHMEM_I(inode);	unsigned long idx;	unsigned long size;	unsigned long limit;	unsigned long stage;	unsigned long diroff;	struct page **dir;	struct page *topdir;	struct page *middir;	struct page *subdir;	swp_entry_t *ptr;	LIST_HEAD(pages_to_free);	long nr_pages_to_free = 0;	long nr_swaps_freed = 0;	int offset;	int freed;	int punch_hole;	spinlock_t *needs_lock;	spinlock_t *punch_lock;	unsigned long upper_limit;	inode->i_ctime = inode->i_mtime = CURRENT_TIME;	idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;	if (idx >= info->next_index)		return;	spin_lock(&info->lock);	info->flags |= SHMEM_TRUNCATE;	if (likely(end == (loff_t) -1)) {		limit = info->next_index;		upper_limit = SHMEM_MAX_INDEX;		info->next_index = idx;		needs_lock = NULL;		punch_hole = 0;	} else {		if (end + 1 >= inode->i_size) {	/* we may free a little more */			limit = (inode->i_size + PAGE_CACHE_SIZE - 1) >>							PAGE_CACHE_SHIFT;			upper_limit = SHMEM_MAX_INDEX;		} else {			limit = (end + 1) >> PAGE_CACHE_SHIFT;			upper_limit = limit;		}		needs_lock = &info->lock;		punch_hole = 1;	}	topdir = info->i_indirect;	if (topdir && idx <= SHMEM_NR_DIRECT && !punch_hole) {		info->i_indirect = NULL;		nr_pages_to_free++;		list_add(&topdir->lru, &pages_to_free);	}	spin_unlock(&info->lock);	if (info->swapped && idx < SHMEM_NR_DIRECT) {		ptr = info->i_direct;		size = limit;		if (size > SHMEM_NR_DIRECT)			size = SHMEM_NR_DIRECT;		nr_swaps_freed = shmem_free_swp(ptr+idx, ptr+size, needs_lock);	}	/*	 * If there are no indirect blocks or we are punching a hole	 * below indirect blocks, nothing to be done.	 */	if (!topdir || limit <= SHMEM_NR_DIRECT)		goto done2;	/*	 * The truncation case has already dropped info->lock, and we're safe	 * because i_size and next_index have already been lowered, preventing	 * access beyond.  But in the punch_hole case, we still need to take	 * the lock when updating the swap directory, because there might be	 * racing accesses by shmem_getpage(SGP_CACHE), shmem_unuse_inode or	 * shmem_writepage.  However, whenever we find we can remove a whole	 * directory page (not at the misaligned start or end of the range),	 * we first NULLify its pointer in the level above, and then have no	 * need to take the lock when updating its contents: needs_lock and	 * punch_lock (either pointing to info->lock or NULL) manage this.	 */	upper_limit -= SHMEM_NR_DIRECT;	limit -= SHMEM_NR_DIRECT;	idx = (idx > SHMEM_NR_DIRECT)? (idx - SHMEM_NR_DIRECT): 0;	offset = idx % ENTRIES_PER_PAGE;	idx -= offset;	dir = shmem_dir_map(topdir);	stage = ENTRIES_PER_PAGEPAGE/2;	if (idx < ENTRIES_PER_PAGEPAGE/2) {		middir = topdir;		diroff = idx/ENTRIES_PER_PAGE;	} else {		dir += ENTRIES_PER_PAGE/2;		dir += (idx - ENTRIES_PER_PAGEPAGE/2)/ENTRIES_PER_PAGEPAGE;		while (stage <= idx)			stage += ENTRIES_PER_PAGEPAGE;		middir = *dir;		if (*dir) {			diroff = ((idx - ENTRIES_PER_PAGEPAGE/2) %				ENTRIES_PER_PAGEPAGE) / ENTRIES_PER_PAGE;			if (!diroff && !offset && upper_limit >= stage) {				if (needs_lock) {					spin_lock(needs_lock);					*dir = NULL;					spin_unlock(needs_lock);					needs_lock = NULL;				} else					*dir = NULL;				nr_pages_to_free++;				list_add(&middir->lru, &pages_to_free);			}			shmem_dir_unmap(dir);			dir = shmem_dir_map(middir);		} else {			diroff = 0;			offset = 0;			idx = stage;		}	}	for (; idx < limit; idx += ENTRIES_PER_PAGE, diroff++) {		if (unlikely(idx == stage)) {			shmem_dir_unmap(dir);			dir = shmem_dir_map(topdir) +			    ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;			while (!*dir) {				dir++;				idx += ENTRIES_PER_PAGEPAGE;				if (idx >= limit)					goto done1;			}			stage = idx + ENTRIES_PER_PAGEPAGE;			middir = *dir;			if (punch_hole)				needs_lock = &info->lock;			if (upper_limit >= stage) {				if (needs_lock) {					spin_lock(needs_lock);					*dir = NULL;					spin_unlock(needs_lock);					needs_lock = NULL;				} else					*dir = NULL;				nr_pages_to_free++;				list_add(&middir->lru, &pages_to_free);			}			shmem_dir_unmap(dir);			cond_resched();			dir = shmem_dir_map(middir);			diroff = 0;		}		punch_lock = needs_lock;		subdir = dir[diroff];		if (subdir && !offset && upper_limit-idx >= ENTRIES_PER_PAGE) {			if (needs_lock) {				spin_lock(needs_lock);				dir[diroff] = NULL;				spin_unlock(needs_lock);				punch_lock = NULL;			} else				dir[diroff] = NULL;			nr_pages_to_free++;			list_add(&subdir->lru, &pages_to_free);		}		if (subdir && page_private(subdir) /* has swap entries */) {			size = limit - idx;			if (size > ENTRIES_PER_PAGE)				size = ENTRIES_PER_PAGE;			freed = shmem_map_and_free_swp(subdir,					offset, size, &dir, punch_lock);			if (!dir)				dir = shmem_dir_map(middir);			nr_swaps_freed += freed;			if (offset || punch_lock) {				spin_lock(&info->lock);				set_page_private(subdir,					page_private(subdir) - freed);				spin_unlock(&info->lock);			} else				BUG_ON(page_private(subdir) != freed);		}		offset = 0;	}done1:	shmem_dir_unmap(dir);done2:	if (inode->i_mapping->nrpages && (info->flags & SHMEM_PAGEIN)) {		/*		 * Call truncate_inode_pages again: racing shmem_unuse_inode		 * may have swizzled a page in from swap since vmtruncate or		 * generic_delete_inode did it, before we lowered next_index.		 * Also, though shmem_getpage checks i_size before adding to		 * cache, no recheck after: so fix the narrow window there too.		 *		 * Recalling truncate_inode_pages_range and unmap_mapping_range		 * every time for punch_hole (which never got a chance to clear		 * SHMEM_PAGEIN at the start of vmtruncate_range) is expensive,		 * yet hardly ever necessary: try to optimize them out later.		 */		truncate_inode_pages_range(inode->i_mapping, start, end);		if (punch_hole)			unmap_mapping_range(inode->i_mapping, start,							end - start, 1);	}	spin_lock(&info->lock);	info->flags &= ~SHMEM_TRUNCATE;	info->swapped -= nr_swaps_freed;	if (nr_pages_to_free)		shmem_free_blocks(inode, nr_pages_to_free);	shmem_recalc_inode(inode);	spin_unlock(&info->lock);	/*	 * Empty swap vector directory pages to be freed?	 */	if (!list_empty(&pages_to_free)) {		pages_to_free.prev->next = NULL;		shmem_free_pages(pages_to_free.next);	}}static void shmem_truncate(struct inode *inode){	shmem_truncate_range(inode, inode->i_size, (loff_t)-1);}static int shmem_notify_change(struct dentry *dentry, struct iattr *attr){	struct inode *inode = dentry->d_inode;	struct page *page = NULL;	int error;	if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {		if (attr->ia_size < inode->i_size) {			/*			 * If truncating down to a partial page, then			 * if that page is already allocated, hold it			 * in memory until the truncation is over, so			 * truncate_partial_page cannnot miss it were			 * it assigned to swap.			 */			if (attr->ia_size & (PAGE_CACHE_SIZE-1)) {				(void) shmem_getpage(inode,					attr->ia_size>>PAGE_CACHE_SHIFT,						&page, SGP_READ, NULL);				if (page)					unlock_page(page);			}			/*			 * Reset SHMEM_PAGEIN flag so that shmem_truncate can			 * detect if any pages might have been added to cache			 * after truncate_inode_pages.  But we needn't bother			 * if it's being fully truncated to zero-length: the			 * nrpages check is efficient enough in that case.			 */			if (attr->ia_size) {				struct shmem_inode_info *info = SHMEM_I(inode);				spin_lock(&info->lock);				info->flags &= ~SHMEM_PAGEIN;				spin_unlock(&info->lock);			}		}	}	error = inode_change_ok(inode, attr);	if (!error)		error = inode_setattr(inode, attr);#ifdef CONFIG_TMPFS_POSIX_ACL	if (!error && (attr->ia_valid & ATTR_MODE))		error = generic_acl_chmod(inode, &shmem_acl_ops);#endif	if (page)		page_cache_release(page);	return error;}static void shmem_delete_inode(struct inode *inode){	struct shmem_inode_info *info = SHMEM_I(inode);	if (inode->i_op->truncate == shmem_truncate) {		truncate_inode_pages(inode->i_mapping, 0);		shmem_unacct_size(info->flags, inode->i_size);		inode->i_size = 0;		shmem_truncate(inode);		if (!list_empty(&info->swaplist)) {			mutex_lock(&shmem_swaplist_mutex);			list_del_init(&info->swaplist);			mutex_unlock(&shmem_swaplist_mutex);		}	}	BUG_ON(inode->i_blocks);	shmem_free_inode(inode->i_sb);	clear_inode(inode);}static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir){	swp_entry_t *ptr;	for (ptr = dir; ptr < edir; ptr++) {		if (ptr->val == entry.val)			return ptr - dir;	}	return -1;}static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page){	struct inode *inode;	unsigned long idx;	unsigned long size;	unsigned long limit;	unsigned long stage;	struct page **dir;	struct page *subdir;	swp_entry_t *ptr;	int offset;	int error;	idx = 0;	ptr = info->i_direct;	spin_lock(&info->lock);	if (!info->swapped) {		list_del_init(&info->swaplist);		goto lost2;	}	limit = info->next_index;	size = limit;	if (size > SHMEM_NR_DIRECT)		size = SHMEM_NR_DIRECT;	offset = shmem_find_swp(entry, ptr, ptr+size);	if (offset >= 0)		goto found;	if (!info->i_indirect)		goto lost2;	dir = shmem_dir_map(info->i_indirect);	stage = SHMEM_NR_DIRECT + ENTRIES_PER_PAGEPAGE/2;	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) {				dir++;				idx += ENTRIES_PER_PAGEPAGE;				if (idx >= limit)					goto lost1;			}			stage = idx + ENTRIES_PER_PAGEPAGE;			subdir = *dir;			shmem_dir_unmap(dir);			dir = shmem_dir_map(subdir);		}		subdir = *dir;		if (subdir && page_private(subdir)) {			ptr = shmem_swp_map(subdir);			size = limit - idx;			if (size > ENTRIES_PER_PAGE)				size = ENTRIES_PER_PAGE;			offset = shmem_find_swp(entry, ptr, ptr+size);			shmem_swp_unmap(ptr);			if (offset >= 0) {				shmem_dir_unmap(dir);				goto found;			}		}	}lost1:	shmem_dir_unmap(dir-1);lost2:	spin_unlock(&info->lock);	return 0;found:	idx += offset;	inode = igrab(&info->vfs_inode);	spin_unlock(&info->lock);	/*	 * Move _head_ to start search for next from here.	 * But be careful: shmem_delete_inode checks list_empty without taking	 * mutex, and there's an instant in list_move_tail when info->swaplist	 * would appear empty, if it were the only one on shmem_swaplist.  We	 * could avoid doing it if inode NULL; or use this minor optimization.	 */	if (shmem_swaplist.next != &info->swaplist)		list_move_tail(&shmem_swaplist, &info->swaplist);	mutex_unlock(&shmem_swaplist_mutex);	error = 1;	if (!inode)		goto out;	/*	 * Charge page using GFP_KERNEL while we can wait.	 * Charged back to the user(not to caller) when swap account is used.	 * add_to_page_cache() will be called with GFP_NOWAIT.	 */	error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL);	if (error)		goto out;	error = radix_tree_preload(GFP_KERNEL);	if (error) {		mem_cgroup_uncharge_cache_page(page);		goto out;	}	error = 1;	spin_lock(&info->lock);	ptr = shmem_swp_entry(info, idx, NULL);	if (ptr && ptr->val == entry.val) {		error = add_to_page_cache_locked(page, inode->i_mapping,						idx, GFP_NOWAIT);		/* does mem_cgroup_uncharge_cache_page on error */	} else	/* we must compensate for our precharge above */		mem_cgroup_uncharge_cache_page(page);	if (error == -EEXIST) {		struct page *filepage = find_get_page(inode->i_mapping, idx);		error = 1;		if (filepage) {			/*			 * There might be a more uptodate page coming down			 * from a stacked writepage: forget our swappage if so.			 */			if (PageUptodate(filepage))				error = 0;			page_cache_release(filepage);		}	}	if (!error) {		delete_from_swap_cache(page);		set_page_dirty(page);		info->flags |= SHMEM_PAGEIN;		shmem_swp_set(info, ptr, 0);		swap_free(entry);		error = 1;	/* not an error, but entry was found */	}	if (ptr)		shmem_swp_unmap(ptr);	spin_unlock(&info->lock);	radix_tree_preload_end();out:	unlock_page(page);	page_cache_release(page);	iput(inode);		/* allows for NULL */	return error;}/* * shmem_unuse() search for an eventually swapped out shmem page. */int shmem_unuse(swp_entry_t entry, struct page *page){	struct list_head *p, *next;	struct shmem_inode_info *info;	int found = 0;	mutex_lock(&shmem_swaplist_mutex);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -