Import 1.3.39
[davej-history.git] / fs / nfs / file.c
blobdcc1d9ed0a202ddce81e967f4ef0c7cf355d98b8
1 /*
2 * linux/fs/nfs/file.c
4 * Copyright (C) 1992 Rick Sladkey
6 * Changes Copyright (C) 1994 by Florian La Roche
7 * - Do not copy data too often around in the kernel.
8 * - In nfs_file_read the return value of kmalloc wasn't checked.
9 * - Put in a better version of read look-ahead buffering. Original idea
10 * and implementation by Wai S Kok elekokws@ee.nus.sg.
12 * Expire cache on write to a file by Wai S Kok (Oct 1994).
14 * nfs regular file handling functions
17 #include <linux/sched.h>
18 #include <linux/kernel.h>
19 #include <linux/errno.h>
20 #include <linux/fcntl.h>
21 #include <linux/stat.h>
22 #include <linux/mm.h>
23 #include <linux/nfs_fs.h>
24 #include <linux/malloc.h>
26 #include <asm/segment.h>
27 #include <asm/system.h>
29 static int nfs_file_read(struct inode *, struct file *, char *, int);
30 static int nfs_file_write(struct inode *, struct file *, const char *, int);
31 static int nfs_fsync(struct inode *, struct file *);
33 static struct file_operations nfs_file_operations = {
34 NULL, /* lseek - default */
35 nfs_file_read, /* read */
36 nfs_file_write, /* write */
37 NULL, /* readdir - bad */
38 NULL, /* select - default */
39 NULL, /* ioctl - default */
40 nfs_mmap, /* mmap */
41 NULL, /* no special open is needed */
42 NULL, /* release */
43 nfs_fsync, /* fsync */
46 struct inode_operations nfs_file_inode_operations = {
47 &nfs_file_operations, /* default file operations */
48 NULL, /* create */
49 NULL, /* lookup */
50 NULL, /* link */
51 NULL, /* unlink */
52 NULL, /* symlink */
53 NULL, /* mkdir */
54 NULL, /* rmdir */
55 NULL, /* mknod */
56 NULL, /* rename */
57 NULL, /* readlink */
58 NULL, /* follow_link */
59 NULL, /* bmap */
60 NULL /* truncate */
63 /* Once data is inserted, it can only be deleted, if (in_use==0). */
64 struct read_cache {
65 int in_use; /* currently in use? */
66 unsigned long inode_num; /* inode number */
67 off_t file_pos; /* file position */
68 int len; /* size of data */
69 unsigned long time; /* time, this entry was inserted */
70 char * buf; /* data */
71 int buf_size; /* size of buffer */
74 #define READ_CACHE_SIZE 5
75 #define EXPIRE_CACHE (HZ * 3) /* keep no longer than 3 seconds */
77 unsigned long num_requests = 0;
78 unsigned long num_cache_hits = 0;
80 static int tail = 0; /* next cache slot to replace */
82 static struct read_cache cache[READ_CACHE_SIZE] = {
83 { 0, 0, -1, 0, 0, NULL, 0 },
84 { 0, 0, -1, 0, 0, NULL, 0 },
85 { 0, 0, -1, 0, 0, NULL, 0 },
86 { 0, 0, -1, 0, 0, NULL, 0 },
87 { 0, 0, -1, 0, 0, NULL, 0 } };
89 static int nfs_fsync(struct inode *inode, struct file *file)
91 return 0;
94 static int nfs_file_read(struct inode *inode, struct file *file, char *buf,
95 int count)
97 int result, hunk, i, n, fs;
98 struct nfs_fattr fattr;
99 char *data;
100 off_t pos;
102 if (!inode) {
103 printk("nfs_file_read: inode = NULL\n");
104 return -EINVAL;
106 if (!S_ISREG(inode->i_mode)) {
107 printk("nfs_file_read: read from non-file, mode %07o\n",
108 inode->i_mode);
109 return -EINVAL;
111 pos = file->f_pos;
112 if (pos + count > inode->i_size)
113 count = inode->i_size - pos;
114 if (count <= 0)
115 return 0;
116 ++num_requests;
117 cli();
118 for (i = 0; i < READ_CACHE_SIZE; i++)
119 if ((cache[i].inode_num == inode->i_ino)
120 && (cache[i].file_pos <= pos)
121 && (cache[i].file_pos + cache[i].len >= pos + count)
122 && (abs(jiffies - cache[i].time) < EXPIRE_CACHE))
123 break;
124 if (i < READ_CACHE_SIZE) {
125 ++cache[i].in_use;
126 sti();
127 ++num_cache_hits;
128 memcpy_tofs(buf, cache[i].buf + pos - cache[i].file_pos, count);
129 --cache[i].in_use;
130 file->f_pos += count;
131 return count;
133 sti();
134 n = NFS_SERVER(inode)->rsize;
135 for (i = 0; i < count - n; i += n) {
136 result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
137 pos, n, buf, &fattr, 1);
138 if (result < 0)
139 return result;
140 pos += result;
141 buf += result;
142 if (result < n) {
143 file->f_pos = pos;
144 nfs_refresh_inode(inode, &fattr);
145 return i + result;
148 fs = 0;
149 if (!(data = (char *)kmalloc(n, GFP_KERNEL))) {
150 data = buf;
151 fs = 1;
153 result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
154 pos, n, data, &fattr, fs);
155 if (result < 0) {
156 if (!fs)
157 kfree_s(data, n);
158 return result;
160 hunk = count - i;
161 if (result < hunk)
162 hunk = result;
163 if (fs) {
164 file->f_pos = pos + hunk;
165 nfs_refresh_inode(inode, &fattr);
166 return i + hunk;
168 memcpy_tofs(buf, data, hunk);
169 file->f_pos = pos + hunk;
170 nfs_refresh_inode(inode, &fattr);
171 cli();
172 if (cache[tail].in_use == 0) {
173 if (cache[tail].buf)
174 kfree_s(cache[tail].buf, cache[tail].buf_size);
175 cache[tail].buf = data;
176 cache[tail].buf_size = n;
177 cache[tail].inode_num = inode->i_ino;
178 cache[tail].file_pos = pos;
179 cache[tail].len = result;
180 cache[tail].time = jiffies;
181 if (++tail >= READ_CACHE_SIZE)
182 tail = 0;
183 } else
184 kfree_s(data, n);
185 sti();
186 return i + hunk;
189 static int nfs_file_write(struct inode *inode, struct file *file, const char *buf,
190 int count)
192 int result, hunk, i, n, pos;
193 struct nfs_fattr fattr;
195 if (!inode) {
196 printk("nfs_file_write: inode = NULL\n");
197 return -EINVAL;
199 if (!S_ISREG(inode->i_mode)) {
200 printk("nfs_file_write: write to non-file, mode %07o\n",
201 inode->i_mode);
202 return -EINVAL;
204 if (count <= 0)
205 return 0;
207 cli();
208 /* If hit, cache is dirty and must be expired. */
209 for (i = 0; i < READ_CACHE_SIZE; i++)
210 if(cache[i].inode_num == inode->i_ino)
211 cache[i].time -= EXPIRE_CACHE;
212 sti();
214 pos = file->f_pos;
215 if (file->f_flags & O_APPEND)
216 pos = inode->i_size;
217 n = NFS_SERVER(inode)->wsize;
218 for (i = 0; i < count; i += n) {
219 hunk = count - i;
220 if (hunk >= n)
221 hunk = n;
222 result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
223 pos, hunk, buf, &fattr);
224 if (result < 0)
225 return result;
226 pos += hunk;
227 buf += hunk;
228 if (hunk < n) {
229 i += hunk;
230 break;
233 file->f_pos = pos;
234 nfs_refresh_inode(inode, &fattr);
235 return i;