mmap.c 2.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11
/*
 *  mmap.c
 *
 *  Copyright (C) 1995, 1996 by Volker Lendecke
 *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
 *
 */

#include <linux/stat.h>
#include <linux/time.h>
#include <linux/kernel.h>
12
#include <linux/gfp.h>
L
Linus Torvalds 已提交
13 14 15 16 17 18
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/fcntl.h>
19
#include <linux/memcontrol.h>
L
Linus Torvalds 已提交
20 21 22

#include <asm/uaccess.h>

23 24
#include "ncp_fs.h"

L
Linus Torvalds 已提交
25 26
/*
 * Fill in the supplied page for mmap
N
Nick Piggin 已提交
27 28
 * XXX: how are we excluding truncate/invalidate here? Maybe need to lock
 * page?
L
Linus Torvalds 已提交
29
 */
N
Nick Piggin 已提交
30 31
static int ncp_file_mmap_fault(struct vm_area_struct *area,
					struct vm_fault *vmf)
L
Linus Torvalds 已提交
32
{
A
Al Viro 已提交
33
	struct inode *inode = file_inode(area->vm_file);
L
Linus Torvalds 已提交
34 35 36 37
	char *pg_addr;
	unsigned int already_read;
	unsigned int count;
	int bufsize;
N
Nick Piggin 已提交
38
	int pos; /* XXX: loff_t ? */
L
Linus Torvalds 已提交
39

N
Nick Piggin 已提交
40 41 42 43 44 45 46 47 48
	/*
	 * ncpfs has nothing against high pages as long
	 * as recvmsg and memset works on it
	 */
	vmf->page = alloc_page(GFP_HIGHUSER);
	if (!vmf->page)
		return VM_FAULT_OOM;
	pg_addr = kmap(vmf->page);
	pos = vmf->pgoff << PAGE_SHIFT;
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

	count = PAGE_SIZE;
	/* what we can read in one go */
	bufsize = NCP_SERVER(inode)->buffer_size;

	already_read = 0;
	if (ncp_make_open(inode, O_RDONLY) >= 0) {
		while (already_read < count) {
			int read_this_time;
			int to_read;

			to_read = bufsize - (pos % bufsize);

			to_read = min_t(unsigned int, to_read, count - already_read);

			if (ncp_read_kernel(NCP_SERVER(inode),
				     NCP_FINFO(inode)->file_handle,
				     pos, to_read,
				     pg_addr + already_read,
				     &read_this_time) != 0) {
				read_this_time = 0;
			}
			pos += read_this_time;
			already_read += read_this_time;

			if (read_this_time < to_read) {
				break;
			}
		}
		ncp_inode_close(inode);

	}

	if (already_read < PAGE_SIZE)
		memset(pg_addr + already_read, 0, PAGE_SIZE - already_read);
N
Nick Piggin 已提交
84 85
	flush_dcache_page(vmf->page);
	kunmap(vmf->page);
L
Linus Torvalds 已提交
86 87 88 89

	/*
	 * If I understand ncp_read_kernel() properly, the above always
	 * fetches from the network, here the analogue of disk.
90
	 * -- nyc
L
Linus Torvalds 已提交
91
	 */
92
	count_vm_event(PGMAJFAULT);
93
	mem_cgroup_count_vm_event(area->vm_mm, PGMAJFAULT);
N
Nick Piggin 已提交
94
	return VM_FAULT_MAJOR;
L
Linus Torvalds 已提交
95 96
}

97
static const struct vm_operations_struct ncp_file_mmap =
L
Linus Torvalds 已提交
98
{
99
	.fault = ncp_file_mmap_fault,
L
Linus Torvalds 已提交
100 101 102 103 104 105
};


/* This is used for a general mmap of a ncp file */
int ncp_mmap(struct file *file, struct vm_area_struct *vma)
{
A
Al Viro 已提交
106
	struct inode *inode = file_inode(file);
L
Linus Torvalds 已提交
107
	
108
	ncp_dbg(1, "called\n");
L
Linus Torvalds 已提交
109 110 111 112 113 114 115 116 117

	if (!ncp_conn_valid(NCP_SERVER(inode)))
		return -EIO;

	/* only PAGE_COW or read-only supported now */
	if (vma->vm_flags & VM_SHARED)
		return -EINVAL;
	/* we do not support files bigger than 4GB... We eventually 
	   supports just 4GB... */
118
	if (vma_pages(vma) + vma->vm_pgoff
L
Linus Torvalds 已提交
119 120 121 122 123 124 125
	   > (1U << (32 - PAGE_SHIFT)))
		return -EFBIG;

	vma->vm_ops = &ncp_file_mmap;
	file_accessed(file);
	return 0;
}