summaryrefslogtreecommitdiffstats
path: root/fs/nfs/file.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
committer <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
commit1513ff9b7899ab588401c89db0e99903dbf5f886 (patch)
treef69cc81a940a502ea23d664c3ffb2d215a479667 /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.c237
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;
+}
+