generic_file_read()
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 16 Jul 2010 22:22:21 +0000 (15:22 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:48 +0000 (17:35 -0700)
Common read function that most FSs will use, utilizing the page cache.

kern/include/vfs.h
kern/src/kfs.c
kern/src/vfs.c

index dd1b2c5..7d8348f 100644 (file)
@@ -424,6 +424,10 @@ struct dentry *get_dentry(struct super_block *sb, struct dentry *parent,
                           char *name);
 void dcache_put(struct dentry *dentry);
 
+/* File functions */
+ssize_t generic_file_read(struct file *file, char *buf, size_t count,
+                          off_t *offset);
+
 /* Page cache functions */
 struct page *pm_find_page(struct page_map *pm, unsigned long index);
 int pm_insert_page(struct page_map *pm, unsigned long index, struct page *page);
index 85c5002..68d19fb 100644 (file)
@@ -535,44 +535,6 @@ off_t kfs_llseek(struct file *file, off_t offset, int whence)
        return temp_off;
 }
 
-/* Read count bytes from the file into buf, starting at *offset, which is increased
- * accordingly. */
-ssize_t kfs_read(struct file *file, char *buf, size_t count, off_t *offset)
-{
-       struct kfs_i_info *k_i_info = (struct kfs_i_info*)file->f_inode->i_fs_info;
-       void *begin, *end;
-
-       /* TODO: handle this higher up in the VFS */
-       if (!count)
-               return 0;
-       if (*offset == file->f_inode->i_size)
-               return 0; /* EOF */
-
-       /* Make sure we don't go past the end of the file */
-       if (*offset + count > file->f_inode->i_size) {
-               count = file->f_inode->i_size - *offset;
-       }
-       begin = k_i_info->filestart + *offset;
-       end = begin + count;
-       if ((begin < k_i_info->filestart) ||
-           (end > k_i_info->filestart + file->f_inode->i_size) ||
-           (end < begin)) {
-               set_errno(current_tf, EINVAL);
-               return -1;
-       }
-
-       /* TODO: think about this.  if it's a user buffer, we're relying on current
-        * to detect whose it is (which should work for async calls).  Consider
-        * pushing this up the VFS stack. */
-       if (current) {
-               memcpy_to_user(current, buf, begin, count);
-       } else {
-               memcpy(buf, begin, count);
-       }
-       *offset += count;
-       return count;
-}
-
 /* Writes count bytes from buf to the file, starting at *offset, which is
  * increased accordingly */
 ssize_t kfs_write(struct file *file, const char *buf, size_t count,
@@ -804,7 +766,7 @@ struct dentry_operations kfs_d_op = {
 
 struct file_operations kfs_f_op = {
        kfs_llseek,
-       kfs_read,
+       generic_file_read,
        kfs_write,
        kfs_readdir,
        kfs_mmap,
index cb682bb..bb2415e 100644 (file)
@@ -12,6 +12,7 @@
 #include <slab.h>
 #include <kmalloc.h>
 #include <kfs.h>
+#include <pmap.h>
 
 struct sb_tailq super_blocks = TAILQ_HEAD_INITIALIZER(super_blocks);
 spinlock_t super_blocks_lock = SPINLOCK_INITIALIZER;
@@ -124,6 +125,8 @@ void qstr_builder(struct dentry *dentry, char *l_name)
        dentry->d_name.len = strnlen(dentry->d_name.name, MAX_FILENAME_SZ);
 }
 
+/* Superblock functions */
+
 /* Helper to alloc and initialize a generic superblock.  This handles all the
  * VFS related things, like lists.  Each FS will need to handle its own things
  * in it's *_get_sb(), usually involving reading off the disc. */
@@ -186,6 +189,8 @@ void init_sb(struct super_block *sb, struct vfsmount *vmnt,
        dcache_put(d_root); // TODO: should set a d_flag too
 }
 
+/* Dentry Functions */
+
 /* Helper to alloc and initialize a generic dentry.
  *
  * If the name is longer than the inline name, it will kmalloc a buffer, so
@@ -235,6 +240,58 @@ void dcache_put(struct dentry *dentry)
        spin_unlock(&dcache_lock);
 }
 
+/* File functions */
+
+/* Read count bytes from the file into buf, starting at *offset, which is increased
+ * accordingly, returning the number of bytes transfered.  Most filesystems will
+ * use this function for their f_op->read.  Note, this uses the page cache. */
+ssize_t generic_file_read(struct file *file, char *buf, size_t count,
+                          off_t *offset)
+{
+       struct page *page;
+       int error;
+       off_t page_off;
+       unsigned long first_idx, last_idx;
+       size_t copy_amt;
+       char *buf_end;
+
+       /* Consider pushing some error checking higher in the VFS */
+       if (!count)
+               return 0;
+       if (*offset == file->f_inode->i_size)
+               return 0; /* EOF */
+       /* Make sure we don't go past the end of the file */
+       if (*offset + count > file->f_inode->i_size) {
+               count = file->f_inode->i_size - *offset;
+       }
+       page_off = *offset & (PGSIZE - 1);
+       first_idx = *offset >> PGSHIFT;
+       last_idx = (*offset + count) >> PGSHIFT;
+       buf_end = buf + count;
+       /* For each file page, make sure it's in the page cache, then copy it out.
+        * TODO: will probably need to consider concurrently truncated files here */
+       for (int i = first_idx; i <= last_idx; i++) {
+               error = file_load_page(file, i, &page);
+               assert(!error); /* TODO: handle ENOMEM and friends */
+               copy_amt = MIN(PGSIZE - page_off, buf_end - buf);
+               /* TODO: think about this.  if it's a user buffer, we're relying on
+                * current to detect whose it is (which should work for async calls). */
+               if (current) {
+                       memcpy_to_user(current, buf, page2kva(page) + page_off, copy_amt);
+               } else {
+                       memcpy(buf, page2kva(page) + page_off, copy_amt);
+               }
+               buf += copy_amt;
+               page_off = 0;
+               page_decref(page);      /* it's still in the cache, we just don't need it */
+       }
+       assert(buf == buf_end);
+       *offset += count;
+       return count;
+}
+
+/* Page cache functions */
+
 /* Looks up the index'th page in the page map, returning an incref'd reference,
  * or 0 if it was not in the map. */
 struct page *pm_find_page(struct page_map *pm, unsigned long index)