inode.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 837 行 · 第 1/2 页

C
837
字号
/* * hugetlbpage-backed filesystem.  Based on ramfs. * * William Irwin, 2002 * * Copyright (C) 2002 Linus Torvalds. */#include <linux/module.h>#include <linux/thread_info.h>#include <asm/current.h>#include <linux/sched.h>		/* remove ASAP */#include <linux/fs.h>#include <linux/mount.h>#include <linux/file.h>#include <linux/writeback.h>#include <linux/pagemap.h>#include <linux/highmem.h>#include <linux/init.h>#include <linux/string.h>#include <linux/backing-dev.h>#include <linux/hugetlb.h>#include <linux/pagevec.h>#include <linux/quotaops.h>#include <linux/slab.h>#include <linux/dnotify.h>#include <linux/statfs.h>#include <linux/security.h>#include <asm/uaccess.h>/* some random number */#define HUGETLBFS_MAGIC	0x958458f6static struct super_operations hugetlbfs_ops;static struct address_space_operations hugetlbfs_aops;struct file_operations hugetlbfs_file_operations;static struct inode_operations hugetlbfs_dir_inode_operations;static struct inode_operations hugetlbfs_inode_operations;static struct backing_dev_info hugetlbfs_backing_dev_info = {	.ra_pages	= 0,	/* No readahead */	.memory_backed	= 1,	/* Does not contribute to dirty memory */};int sysctl_hugetlb_shm_group;static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma){	struct inode *inode = file->f_dentry->d_inode;	struct address_space *mapping = inode->i_mapping;	loff_t len, vma_len;	int ret;	if ((vma->vm_flags & (VM_MAYSHARE | VM_WRITE)) == VM_WRITE)		return -EINVAL;	if (vma->vm_pgoff & (HPAGE_SIZE / PAGE_SIZE - 1))		return -EINVAL;	if (vma->vm_start & ~HPAGE_MASK)		return -EINVAL;	if (vma->vm_end & ~HPAGE_MASK)		return -EINVAL;	if (vma->vm_end - vma->vm_start < HPAGE_SIZE)		return -EINVAL;	vma_len = (loff_t)(vma->vm_end - vma->vm_start);	down(&inode->i_sem);	file_accessed(file);	vma->vm_flags |= VM_HUGETLB | VM_RESERVED;	vma->vm_ops = &hugetlb_vm_ops;	ret = -ENOMEM;	len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);	if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size)		goto out;	ret = hugetlb_prefault(mapping, vma);	if (ret)		goto out;	if (inode->i_size < len)		inode->i_size = len;out:	up(&inode->i_sem);	return ret;}/* * Called under down_write(mmap_sem), page_table_lock is not held */#ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREAunsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,		unsigned long len, unsigned long pgoff, unsigned long flags);#elsestatic unsigned longhugetlb_get_unmapped_area(struct file *file, unsigned long addr,		unsigned long len, unsigned long pgoff, unsigned long flags){	struct mm_struct *mm = current->mm;	struct vm_area_struct *vma;	if (len & ~HPAGE_MASK)		return -EINVAL;	if (len > TASK_SIZE)		return -ENOMEM;	if (addr) {		addr = ALIGN(addr, HPAGE_SIZE);		vma = find_vma(mm, addr);		if (TASK_SIZE - len >= addr &&		    (!vma || addr + len <= vma->vm_start))			return addr;	}	addr = ALIGN(mm->free_area_cache, HPAGE_SIZE);	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {		/* At this point:  (!vma || addr < vma->vm_end). */		if (TASK_SIZE - len < addr)			return -ENOMEM;		if (!vma || addr + len <= vma->vm_start)			return addr;		addr = ALIGN(vma->vm_end, HPAGE_SIZE);	}}#endif/* * Read a page. Again trivial. If it didn't already exist * in the page cache, it is zero-filled. */static int hugetlbfs_readpage(struct file *file, struct page * page){	unlock_page(page);	return -EINVAL;}static int hugetlbfs_prepare_write(struct file *file,			struct page *page, unsigned offset, unsigned to){	return -EINVAL;}static int hugetlbfs_commit_write(struct file *file,			struct page *page, unsigned offset, unsigned to){	return -EINVAL;}void huge_pagevec_release(struct pagevec *pvec){	int i;	for (i = 0; i < pagevec_count(pvec); ++i)		put_page(pvec->pages[i]);	pagevec_reinit(pvec);}void truncate_huge_page(struct page *page){	clear_page_dirty(page);	ClearPageUptodate(page);	remove_from_page_cache(page);	put_page(page);}void truncate_hugepages(struct address_space *mapping, loff_t lstart){	const pgoff_t start = lstart >> HPAGE_SHIFT;	struct pagevec pvec;	pgoff_t next;	int i;	pagevec_init(&pvec, 0);	next = start;	while (1) {		if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {			if (next == start)				break;			next = start;			continue;		}		for (i = 0; i < pagevec_count(&pvec); ++i) {			struct page *page = pvec.pages[i];			lock_page(page);			if (page->index > next)				next = page->index;			++next;			truncate_huge_page(page);			unlock_page(page);			hugetlb_put_quota(mapping);		}		huge_pagevec_release(&pvec);	}	BUG_ON(!lstart && mapping->nrpages);}static void hugetlbfs_delete_inode(struct inode *inode){	struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(inode->i_sb);	hlist_del_init(&inode->i_hash);	list_del_init(&inode->i_list);	inode->i_state |= I_FREEING;	inodes_stat.nr_inodes--;	spin_unlock(&inode_lock);	if (inode->i_data.nrpages)		truncate_hugepages(&inode->i_data, 0);	security_inode_delete(inode);	if (sbinfo->free_inodes >= 0) {		spin_lock(&sbinfo->stat_lock);		sbinfo->free_inodes++;		spin_unlock(&sbinfo->stat_lock);	}	clear_inode(inode);	destroy_inode(inode);}static void hugetlbfs_forget_inode(struct inode *inode){	struct super_block *super_block = inode->i_sb;	struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(super_block);	if (hlist_unhashed(&inode->i_hash))		goto out_truncate;	if (!(inode->i_state & (I_DIRTY|I_LOCK))) {		list_del(&inode->i_list);		list_add(&inode->i_list, &inode_unused);	}	inodes_stat.nr_unused++;	if (!super_block || (super_block->s_flags & MS_ACTIVE)) {		spin_unlock(&inode_lock);		return;	}	/* write_inode_now() ? */	inodes_stat.nr_unused--;	hlist_del_init(&inode->i_hash);out_truncate:	list_del_init(&inode->i_list);	inode->i_state |= I_FREEING;	inodes_stat.nr_inodes--;	spin_unlock(&inode_lock);	if (inode->i_data.nrpages)		truncate_hugepages(&inode->i_data, 0);	if (sbinfo->free_inodes >= 0) {		spin_lock(&sbinfo->stat_lock);		sbinfo->free_inodes++;		spin_unlock(&sbinfo->stat_lock);	}	clear_inode(inode);	destroy_inode(inode);}static void hugetlbfs_drop_inode(struct inode *inode){	if (!inode->i_nlink)		hugetlbfs_delete_inode(inode);	else		hugetlbfs_forget_inode(inode);}/* * h_pgoff is in HPAGE_SIZE units. * vma->vm_pgoff is in PAGE_SIZE units. */static inline voidhugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff){	struct vm_area_struct *vma;	struct prio_tree_iter iter;	vma_prio_tree_foreach(vma, &iter, root, h_pgoff, ULONG_MAX) {		unsigned long h_vm_pgoff;		unsigned long v_length;		unsigned long v_offset;		h_vm_pgoff = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT);		v_offset = (h_pgoff - h_vm_pgoff) << HPAGE_SHIFT;		/*		 * Is this VMA fully outside the truncation point?		 */		if (h_vm_pgoff >= h_pgoff)			v_offset = 0;		v_length = vma->vm_end - vma->vm_start;		zap_hugepage_range(vma,				vma->vm_start + v_offset,				v_length - v_offset);	}}/* * Expanding truncates are not allowed. */static int hugetlb_vmtruncate(struct inode *inode, loff_t offset){	unsigned long pgoff;	struct address_space *mapping = inode->i_mapping;	if (offset > inode->i_size)		return -EINVAL;	BUG_ON(offset & ~HPAGE_MASK);	pgoff = offset >> HPAGE_SHIFT;	inode->i_size = offset;	spin_lock(&mapping->i_mmap_lock);	if (!prio_tree_empty(&mapping->i_mmap))		hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff);	spin_unlock(&mapping->i_mmap_lock);	truncate_hugepages(mapping, offset);	return 0;}static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr){	struct inode *inode = dentry->d_inode;	int error;	unsigned int ia_valid = attr->ia_valid;	BUG_ON(!inode);	error = inode_change_ok(inode, attr);	if (error)		goto out;	if (ia_valid & ATTR_SIZE) {		error = -EINVAL;		if (!(attr->ia_size & ~HPAGE_MASK))			error = hugetlb_vmtruncate(inode, attr->ia_size);		if (error)			goto out;		attr->ia_valid &= ~ATTR_SIZE;	}	error = inode_setattr(inode, attr);out:	return error;}static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, 					gid_t gid, int mode, dev_t dev){	struct inode *inode;	struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb);	if (sbinfo->free_inodes >= 0) {		spin_lock(&sbinfo->stat_lock);		if (!sbinfo->free_inodes) {			spin_unlock(&sbinfo->stat_lock);			return NULL;		}		sbinfo->free_inodes--;		spin_unlock(&sbinfo->stat_lock);	}	inode = new_inode(sb);	if (inode) {		struct hugetlbfs_inode_info *info;		inode->i_mode = mode;		inode->i_uid = uid;		inode->i_gid = gid;		inode->i_blksize = HPAGE_SIZE;		inode->i_blocks = 0;		inode->i_mapping->a_ops = &hugetlbfs_aops;		inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info;		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;		info = HUGETLBFS_I(inode);		mpol_shared_policy_init(&info->policy);		switch (mode & S_IFMT) {		default:			init_special_inode(inode, mode, dev);			break;		case S_IFREG:			inode->i_op = &hugetlbfs_inode_operations;			inode->i_fop = &hugetlbfs_file_operations;			break;		case S_IFDIR:			inode->i_op = &hugetlbfs_dir_inode_operations;			inode->i_fop = &simple_dir_operations;			/* directory inodes start off with i_nlink == 2 (for "." entry) */			inode->i_nlink++;			break;		case S_IFLNK:			inode->i_op = &page_symlink_inode_operations;			break;		}	}	return inode;}/* * File creation. Allocate an inode, and we're done.. */static int hugetlbfs_mknod(struct inode *dir,			struct dentry *dentry, int mode, dev_t dev){	struct inode *inode;	int error = -ENOSPC;	gid_t gid;

⌨️ 快捷键说明

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