/* * 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 */ #ifdef MODULE #include #endif #include #include #include #include #include #include #include #include #include #include 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; }