historical/gems-kernel.git/source/THIRDPARTY/linux-old/fs/nfs/mmap.c

156 lines
3.7 KiB
C

/*
* fs/nfs/mmap.c by Jon Tombs 15 Aug 1993
*
* This code is from
* linux/mm/mmap.c which was written by obz, Linus and Eric
* and
* linux/mm/memory.c by Linus Torvalds and others
*
* Copyright (C) 1993
*
*/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/nfs_fs.h>
#include <asm/segment.h>
#include <asm/system.h>
extern int share_page(struct vm_area_struct * area, struct task_struct * tsk,
struct inode * inode, unsigned long address, unsigned long error_code,
unsigned long newpage);
extern unsigned long put_page(struct task_struct * tsk,unsigned long page,
unsigned long address,int prot);
static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
unsigned long address);
extern void file_mmap_free(struct vm_area_struct * area);
extern int file_mmap_share(struct vm_area_struct * from, struct vm_area_struct * to,
unsigned long address);
struct vm_operations_struct nfs_file_mmap = {
NULL, /* open */
file_mmap_free, /* close */
nfs_file_mmap_nopage, /* nopage */
NULL, /* wppage */
file_mmap_share, /* share */
NULL, /* unmap */
};
/* This is used for a general mmap of a nfs file */
int nfs_mmap(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct * mpnt;
if (prot & PAGE_RW) /* only PAGE_COW or read-only supported now */
return -EINVAL;
if (off & (inode->i_sb->s_blocksize - 1))
return -EINVAL;
if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return -ENOMEM;
unmap_page_range(addr, len);
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = inode;
inode->i_count++;
mpnt->vm_offset = off;
mpnt->vm_ops = &nfs_file_mmap;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, NULL, NULL);
return 0;
}
static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
unsigned long address)
{
struct inode * inode = area->vm_inode;
unsigned int clear;
unsigned long page;
unsigned long tmp;
int n;
int i;
int pos;
struct nfs_fattr fattr;
address &= PAGE_MASK;
pos = address - area->vm_start + area->vm_offset;
page = get_free_page(GFP_KERNEL);
if (share_page(area, area->vm_task, inode, address, error_code, page)) {
++area->vm_task->min_flt;
return;
}
++area->vm_task->maj_flt;
if (!page) {
oom(current);
put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE);
return;
}
clear = 0;
if (address + PAGE_SIZE > area->vm_end) {
clear = address + PAGE_SIZE - area->vm_end;
}
n = NFS_SERVER(inode)->rsize; /* what we can read in one go */
for (i = 0; i < (PAGE_SIZE - clear); i += n) {
int hunk, result;
hunk = PAGE_SIZE - i;
if (hunk > n)
hunk = n;
result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
pos, hunk, (char *) (page + i), &fattr);
if (result < 0)
break;
pos += result;
if (result < n) {
i += result;
break;
}
}
#ifdef doweneedthishere
nfs_refresh_inode(inode, &fattr);
#endif
if (!(error_code & PAGE_RW)) {
if (share_page(area, area->vm_task, inode, address, error_code, page))
return;
}
tmp = page + PAGE_SIZE;
while (clear--) {
*(char *)--tmp = 0;
}
if (put_page(area->vm_task,page,address,area->vm_page_prot))
return;
free_page(page);
oom(current);
}