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

📄 shmem.c

📁 ARM 嵌入式 系统 设计与实例开发 实验教材 二源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Resizable virtual memory filesystem for Linux. * * Copyright (C) 2000 Linus Torvalds. *		 2000 Transmeta Corp. *		 2000-2001 Christoph Rohland *		 2000-2001 SAP AG *  * This file is released under the GPL. *//* * This virtual memory filesystem is heavily based on the ramfs. It * extends ramfs by the ability to use swap and honor resource limits * which makes it a completely usable filesystem. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/devfs_fs_kernel.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/file.h>#include <linux/swap.h>#include <linux/pagemap.h>#include <linux/string.h>#include <linux/locks.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>/* This magic number is used in glibc for posix shared memory */#define TMPFS_MAGIC	0x01021994#define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long))#define SHMEM_SB(sb) (&sb->u.shmem_sb)static struct super_operations shmem_ops;static struct address_space_operations shmem_aops;static struct file_operations shmem_file_operations;static struct inode_operations shmem_inode_operations;static struct file_operations shmem_dir_operations;static struct inode_operations shmem_dir_inode_operations;static struct vm_operations_struct shmem_vm_ops;LIST_HEAD (shmem_inodes);static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED;atomic_t shmem_nrpages = ATOMIC_INIT(0); /* Not used right now */#define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512)/* * shmem_recalc_inode - recalculate the size of an inode * * @inode: inode to recalc * @swap:  additional swap pages freed externally * * We have to calculate the free blocks since the mm can drop pages * behind our back * * But we know that normally * inodes->i_blocks/BLOCKS_PER_PAGE ==  * 			inode->i_mapping->nrpages + info->swapped * * So the mm freed  * inodes->i_blocks/BLOCKS_PER_PAGE -  * 			(inode->i_mapping->nrpages + info->swapped) * * It has to be called with the spinlock held. */static void shmem_recalc_inode(struct inode * inode){	unsigned long freed;	freed = (inode->i_blocks/BLOCKS_PER_PAGE) -		(inode->i_mapping->nrpages + SHMEM_I(inode)->swapped);	if (freed){		struct shmem_sb_info * sbinfo = SHMEM_SB(inode->i_sb);		inode->i_blocks -= freed*BLOCKS_PER_PAGE;		spin_lock (&sbinfo->stat_lock);		sbinfo->free_blocks += freed;		spin_unlock (&sbinfo->stat_lock);	}}/* * shmem_swp_entry - find the swap vector position in the info structure * * @info:  info structure for the inode * @index: index of the page to find * @page:  optional page to add to the structure. Has to be preset to *         all zeros * * If there is no space allocated yet it will return -ENOMEM when * page == 0 else it will use the page for the needed block. * * returns -EFBIG if the index is too big. * * * The swap vector is organized the following way: * * There are SHMEM_NR_DIRECT entries directly stored in the * shmem_inode_info structure. So small files do not need an addional * allocation. * * For pages with index > SHMEM_NR_DIRECT there is the pointer * i_indirect which points to a page which holds in the first half * doubly indirect blocks, in the second half triple indirect blocks: * * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the * following layout (for SHMEM_NR_DIRECT == 16): * * i_indirect -> dir --> 16-19 * 	      |	     +-> 20-23 * 	      | * 	      +-->dir2 --> 24-27 * 	      |	       +-> 28-31 * 	      |	       +-> 32-35 * 	      |	       +-> 36-39 * 	      | * 	      +-->dir3 --> 40-43 * 	       	       +-> 44-47 * 	      	       +-> 48-51 * 	      	       +-> 52-55 */#define SHMEM_MAX_BLOCKS (SHMEM_NR_DIRECT + ENTRIES_PER_PAGE * ENTRIES_PER_PAGE/2*(ENTRIES_PER_PAGE+1))static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long index, unsigned long page) {	unsigned long offset;	void **dir;	if (index < SHMEM_NR_DIRECT)		return info->i_direct+index;	index -= SHMEM_NR_DIRECT;	offset = index % ENTRIES_PER_PAGE;	index /= ENTRIES_PER_PAGE;	if (!info->i_indirect) {		info->i_indirect = (void *) page;		return ERR_PTR(-ENOMEM);	}	dir = info->i_indirect + index;	if (index >= ENTRIES_PER_PAGE/2) {		index -= ENTRIES_PER_PAGE/2;		dir = info->i_indirect + ENTRIES_PER_PAGE/2 			+ index/ENTRIES_PER_PAGE;		index %= ENTRIES_PER_PAGE;		if(!*dir) {			*dir = (void *) page;			/* We return since we will need another page                           in the next step */			return ERR_PTR(-ENOMEM);		}		dir = ((void **)*dir) + index;	}	if (!*dir) {		if (!page)			return ERR_PTR(-ENOMEM);		*dir = (void *)page;	}	return ((swp_entry_t *)*dir) + offset;}/* * shmem_alloc_entry - get the position of the swap entry for the *                     page. If it does not exist allocate the entry * * @info:	info structure for the inode * @index:	index of the page to find */static inline swp_entry_t * shmem_alloc_entry (struct shmem_inode_info *info, unsigned long index){	unsigned long page = 0;	swp_entry_t * res;	if (index >= SHMEM_MAX_BLOCKS)		return ERR_PTR(-EFBIG);	if (info->next_index <= index)		info->next_index = index + 1;	while ((res = shmem_swp_entry(info,index,page)) == ERR_PTR(-ENOMEM)) {		page = get_zeroed_page(GFP_USER);		if (!page)			break;	}	return res;}/* * shmem_free_swp - free some swap entries in a directory * * @dir:   pointer to the directory * @count: number of entries to scan */static int shmem_free_swp(swp_entry_t *dir, unsigned int count){	swp_entry_t *ptr, entry;	int freed = 0;	for (ptr = dir; ptr < dir + count; ptr++) {		if (!ptr->val)			continue;		entry = *ptr;		*ptr = (swp_entry_t){0};		freed++;		free_swap_and_cache(entry);	}	return freed;}/* * shmem_truncate_direct - free the swap entries of a whole doubly *                         indirect block * * @dir:	pointer to the pointer to the block * @start:	offset to start from (in pages) * @len:	how many pages are stored in this block * * Returns the number of freed swap entries. */static inline unsigned long shmem_truncate_direct(swp_entry_t *** dir, unsigned long start, unsigned long len) {	swp_entry_t **last, **ptr;	unsigned long off, freed = 0; 	if (!*dir)		return 0;	last = *dir + (len + ENTRIES_PER_PAGE-1) / ENTRIES_PER_PAGE;	off = start % ENTRIES_PER_PAGE;	for (ptr = *dir + start/ENTRIES_PER_PAGE; ptr < last; ptr++) {		if (!*ptr) {			off = 0;			continue;		}		if (!off) {			freed += shmem_free_swp(*ptr, ENTRIES_PER_PAGE);			free_page ((unsigned long) *ptr);			*ptr = 0;		} else {			freed += shmem_free_swp(*ptr+off,ENTRIES_PER_PAGE-off);			off = 0;		}	}		if (!start) {		free_page((unsigned long) *dir);		*dir = 0;	}	return freed;}/* * shmem_truncate_indirect - truncate an inode * * @info:  the info structure of the inode * @index: the index to truncate * * This function locates the last doubly indirect block and calls * then shmem_truncate_direct to do the real work */static inline unsigned longshmem_truncate_indirect(struct shmem_inode_info *info, unsigned long index){	swp_entry_t ***base;	unsigned long baseidx, len, start;	unsigned long max = info->next_index-1;	if (max < SHMEM_NR_DIRECT) {		info->next_index = index;		return shmem_free_swp(info->i_direct + index,				      SHMEM_NR_DIRECT - index);	}	if (max < ENTRIES_PER_PAGE * ENTRIES_PER_PAGE/2 + SHMEM_NR_DIRECT) {		max -= SHMEM_NR_DIRECT;		base = (swp_entry_t ***) &info->i_indirect;		baseidx = SHMEM_NR_DIRECT;		len = max+1;	} else {		max -= ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2+SHMEM_NR_DIRECT;		if (max >= ENTRIES_PER_PAGE*ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2)			BUG();		baseidx = max & ~(ENTRIES_PER_PAGE*ENTRIES_PER_PAGE-1);		base = (swp_entry_t ***) info->i_indirect + ENTRIES_PER_PAGE/2 + baseidx/ENTRIES_PER_PAGE/ENTRIES_PER_PAGE ;		len = max - baseidx + 1;		baseidx += ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2+SHMEM_NR_DIRECT;	}	if (index > baseidx) {		info->next_index = index;		start = index - baseidx;	} else {		info->next_index = baseidx;		start = 0;	}	return shmem_truncate_direct(base, start, len);}static void shmem_truncate (struct inode * inode){	unsigned long index;	unsigned long freed = 0;	struct shmem_inode_info * info = SHMEM_I(inode);	down(&info->sem);	inode->i_ctime = inode->i_mtime = CURRENT_TIME;	spin_lock (&info->lock);	index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;	while (index < info->next_index) 		freed += shmem_truncate_indirect(info, index);	info->swapped -= freed;	shmem_recalc_inode(inode);	spin_unlock (&info->lock);	up(&info->sem);}static void shmem_delete_inode(struct inode * inode){	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);	inode->i_size = 0;	if (inode->i_op->truncate == shmem_truncate){ 		spin_lock (&shmem_ilock);		list_del (&SHMEM_I(inode)->list);		spin_unlock (&shmem_ilock);		shmem_truncate (inode);	}	spin_lock (&sbinfo->stat_lock);	sbinfo->free_inodes++;	spin_unlock (&sbinfo->stat_lock);	clear_inode(inode);}static int shmem_clear_swp (swp_entry_t entry, swp_entry_t *ptr, int size) {	swp_entry_t *test;	for (test = ptr; test < ptr + size; test++) {		if (test->val == entry.val) {			swap_free (entry);			*test = (swp_entry_t) {0};			return test - ptr;		}	}	return -1;}static int shmem_unuse_inode (struct shmem_inode_info *info, swp_entry_t entry, struct page *page){	swp_entry_t *ptr;	unsigned long idx;	int offset;		idx = 0;	spin_lock (&info->lock);	offset = shmem_clear_swp (entry, info->i_direct, SHMEM_NR_DIRECT);	if (offset >= 0)		goto found;	for (idx = SHMEM_NR_DIRECT; idx < info->next_index; 	     idx += ENTRIES_PER_PAGE) {		ptr = shmem_swp_entry(info, idx, 0);		if (IS_ERR(ptr))			continue;		offset = shmem_clear_swp (entry, ptr, ENTRIES_PER_PAGE);		if (offset >= 0)			goto found;	}	spin_unlock (&info->lock);	return 0;found:	delete_from_swap_cache(page);	add_to_page_cache(page, info->inode->i_mapping, offset + idx);	SetPageDirty(page);	SetPageUptodate(page);	info->swapped--;	spin_unlock(&info->lock);	return 1;}/* * unuse_shmem() search for an eventually swapped out shmem page. */void shmem_unuse(swp_entry_t entry, struct page *page){	struct list_head *p;	struct shmem_inode_info * info;	spin_lock (&shmem_ilock);	list_for_each(p, &shmem_inodes) {		info = list_entry(p, struct shmem_inode_info, list);		if (shmem_unuse_inode(info, entry, page))			break;	}	spin_unlock (&shmem_ilock);}/* * Move the page from the page cache to the swap cache. * * The page lock prevents multiple occurences of shmem_writepage at * once.  We still need to guard against racing with * shmem_getpage_locked().   */static int shmem_writepage(struct page * page){	struct shmem_inode_info *info;	swp_entry_t *entry, swap;	struct address_space *mapping;	unsigned long index;	struct inode *inode;	if (!PageLocked(page))		BUG();	if (!PageLaunder(page))		return fail_writepage(page);	mapping = page->mapping;	index = page->index;	inode = mapping->host;	info = SHMEM_I(inode);	if (info->locked)		return fail_writepage(page);getswap:	swap = get_swap_page();	if (!swap.val)		return fail_writepage(page);	spin_lock(&info->lock);	entry = shmem_swp_entry(info, index, 0);	if (IS_ERR(entry))	/* this had been allocated on page allocation */		BUG();	shmem_recalc_inode(inode);	if (entry->val)		BUG();	/* Remove it from the page cache */	remove_inode_page(page);	page_cache_release(page);	/* Add it to the swap cache */	if (add_to_swap_cache(page, swap) != 0) {		/*		 * Raced with "speculative" read_swap_cache_async.		 * Add page back to page cache, unref swap, try again.		 */		add_to_page_cache_locked(page, mapping, index);		spin_unlock(&info->lock);		swap_free(swap);		goto getswap;	}	*entry = swap;	info->swapped++;	spin_unlock(&info->lock);	SetPageUptodate(page);	set_page_dirty(page);	UnlockPage(page);	return 0;}/* * shmem_getpage_locked - either get the page from swap or allocate a new one * * If we allocate a new one we do not mark it dirty. That's up to the * vm. If we swap it in we mark it dirty since we also free the swap * entry since a page cannot live in both the swap and page cache * * Called with the inode locked, so it cannot race with itself, but we * still need to guard against racing with shm_writepage(), which might * be trying to move the page to the swap cache as we run. */static struct page * shmem_getpage_locked(struct shmem_inode_info *info, struct inode * inode, unsigned long idx){	struct address_space * mapping = inode->i_mapping;	struct shmem_sb_info *sbinfo;	struct page * page;	swp_entry_t *entry;repeat:	page = find_lock_page(mapping, idx);	if (page)		return page;	entry = shmem_alloc_entry (info, idx);	if (IS_ERR(entry))		return (void *)entry;	spin_lock (&info->lock);

⌨️ 快捷键说明

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