diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /fs/nfs/file.c |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/nfs/file.c')
-rw-r--r-- | fs/nfs/file.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/fs/nfs/file.c b/fs/nfs/file.c new file mode 100644 index 000000000..e71d29483 --- /dev/null +++ b/fs/nfs/file.c @@ -0,0 +1,237 @@ +/* + * linux/fs/nfs/file.c + * + * Copyright (C) 1992 Rick Sladkey + * + * Changes Copyright (C) 1994 by Florian La Roche + * - Do not copy data too often around in the kernel. + * - In nfs_file_read the return value of kmalloc wasn't checked. + * - Put in a better version of read look-ahead buffering. Original idea + * and implementation by Wai S Kok elekokws@ee.nus.sg. + * + * Expire cache on write to a file by Wai S Kok (Oct 1994). + * + * nfs regular file handling functions + */ + +#include <asm/segment.h> +#include <asm/system.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/stat.h> +#include <linux/mm.h> +#include <linux/nfs_fs.h> +#include <linux/malloc.h> + +static int nfs_file_read(struct inode *, struct file *, char *, int); +static int nfs_file_write(struct inode *, struct file *, char *, int); +static int nfs_fsync(struct inode *, struct file *); + +static struct file_operations nfs_file_operations = { + NULL, /* lseek - default */ + nfs_file_read, /* read */ + nfs_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + nfs_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + nfs_fsync, /* fsync */ +}; + +struct inode_operations nfs_file_inode_operations = { + &nfs_file_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL /* truncate */ +}; + +/* Once data is inserted, it can only be deleted, if (in_use==0). */ +struct read_cache { + int in_use; /* currently in use? */ + unsigned long inode_num; /* inode number */ + off_t file_pos; /* file position */ + int len; /* size of data */ + unsigned long time; /* time, this entry was inserted */ + char * buf; /* data */ + int buf_size; /* size of buffer */ +}; + +#define READ_CACHE_SIZE 5 +#define EXPIRE_CACHE (HZ * 3) /* keep no longer than 3 seconds */ + +unsigned long num_requests = 0; +unsigned long num_cache_hits = 0; + +static int tail = 0; /* next cache slot to replace */ + +static struct read_cache cache[READ_CACHE_SIZE] = { + { 0, 0, -1, 0, 0, NULL, 0 }, + { 0, 0, -1, 0, 0, NULL, 0 }, + { 0, 0, -1, 0, 0, NULL, 0 }, + { 0, 0, -1, 0, 0, NULL, 0 }, + { 0, 0, -1, 0, 0, NULL, 0 } }; + +static int nfs_fsync(struct inode *inode, struct file *file) +{ + return 0; +} + +static int nfs_file_read(struct inode *inode, struct file *file, char *buf, + int count) +{ + int result, hunk, i, n, fs; + struct nfs_fattr fattr; + char *data; + off_t pos; + + if (!inode) { + printk("nfs_file_read: inode = NULL\n"); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("nfs_file_read: read from non-file, mode %07o\n", + inode->i_mode); + return -EINVAL; + } + pos = file->f_pos; + if (pos + count > inode->i_size) + count = inode->i_size - pos; + if (count <= 0) + return 0; + ++num_requests; + cli(); + for (i = 0; i < READ_CACHE_SIZE; i++) + if ((cache[i].inode_num == inode->i_ino) + && (cache[i].file_pos <= pos) + && (cache[i].file_pos + cache[i].len >= pos + count) + && (abs(jiffies - cache[i].time) <= EXPIRE_CACHE)) + break; + if (i < READ_CACHE_SIZE) { + ++cache[i].in_use; + sti(); + ++num_cache_hits; + memcpy_tofs(buf, cache[i].buf + pos - cache[i].file_pos, count); + --cache[i].in_use; + file->f_pos += count; + return count; + } + sti(); + n = NFS_SERVER(inode)->rsize; + for (i = 0; i < count - n; i += n) { + result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), + pos, n, buf, &fattr, 1); + if (result < 0) + return result; + pos += result; + buf += result; + if (result < n) { + file->f_pos = pos; + nfs_refresh_inode(inode, &fattr); + return i + result; + } + } + fs = 0; + if (!(data = (char *)kmalloc(n, GFP_KERNEL))) { + data = buf; + fs = 1; + } + result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), + pos, n, data, &fattr, fs); + if (result < 0) { + if (!fs) + kfree_s(data, n); + return result; + } + hunk = count - i; + if (result < hunk) + hunk = result; + if (fs) { + file->f_pos = pos + hunk; + nfs_refresh_inode(inode, &fattr); + return i + hunk; + } + memcpy_tofs(buf, data, hunk); + file->f_pos = pos + hunk; + nfs_refresh_inode(inode, &fattr); + cli(); + if (cache[tail].in_use == 0) { + if (cache[tail].buf) + kfree_s(cache[tail].buf, cache[tail].buf_size); + cache[tail].buf = data; + cache[tail].buf_size = n; + cache[tail].inode_num = inode->i_ino; + cache[tail].file_pos = pos; + cache[tail].len = result; + cache[tail].time = jiffies; + if (++tail >= READ_CACHE_SIZE) + tail = 0; + } else + kfree_s(data, n); + sti(); + return i + hunk; +} + +static int nfs_file_write(struct inode *inode, struct file *file, char *buf, + int count) +{ + int result, hunk, i, n, pos; + struct nfs_fattr fattr; + + if (!inode) { + printk("nfs_file_write: inode = NULL\n"); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("nfs_file_write: write to non-file, mode %07o\n", + inode->i_mode); + return -EINVAL; + } + if (count <= 0) + return 0; + + cli(); + /* If hit, cache is dirty and must be expired. */ + for (i = 0; i < READ_CACHE_SIZE; i++) + if(cache[i].inode_num == inode->i_ino) + cache[i].time -= EXPIRE_CACHE; + sti(); + + pos = file->f_pos; + if (file->f_flags & O_APPEND) + pos = inode->i_size; + n = NFS_SERVER(inode)->wsize; + for (i = 0; i < count; i += n) { + hunk = count - i; + if (hunk >= n) + hunk = n; + result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode), + pos, hunk, buf, &fattr); + if (result < 0) + return result; + pos += hunk; + buf += hunk; + if (hunk < n) { + i += hunk; + break; + } + } + file->f_pos = pos; + nfs_refresh_inode(inode, &fattr); + return i; +} + |