Checkpointing changes
[yaffs2.git] / yaffs_fs.c
index 574eda6..0959111 100644 (file)
@@ -31,7 +31,7 @@
  */
 
 const char *yaffs_fs_c_version =
-    "$Id: yaffs_fs.c,v 1.29 2005-08-11 01:07:43 marty Exp $";
+    "$Id: yaffs_fs.c,v 1.46 2006-05-08 10:13:34 charles Exp $";
 extern const char *yaffs_guts_c_version;
 
 #include <linux/config.h>
@@ -48,6 +48,7 @@ extern const char *yaffs_guts_c_version;
 #include <linux/mtd/mtd.h>
 #include <linux/interrupt.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 
@@ -72,14 +73,14 @@ extern const char *yaffs_guts_c_version;
 #include "yportenv.h"
 #include "yaffs_guts.h"
 
-unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS;
-/*unsigned yaffs_traceMask = 0xFFFFFFFF; */
+unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | 
+                          YAFFS_TRACE_BAD_BLOCKS | 
+                          YAFFS_TRACE_CHECKPOINT
+                          /* | 0xFFFFFFFF */; 
 
-#ifdef CONFIG_YAFFS_YAFFS1
 #include <linux/mtd/mtd.h>
 #include "yaffs_mtdif.h"
 #include "yaffs_mtdif2.h"
-#endif                         /*CONFIG_YAFFS_YAFFS1 */
 
 /*#define T(x) printk x */
 
@@ -155,7 +156,11 @@ static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset,
 
 static int yaffs_readlink(struct dentry *dentry, char __user * buffer,
                          int buflen);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13))
+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd);
+#else
 static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd);
+#endif
 
 static struct address_space_operations yaffs_file_address_operations = {
        .readpage = yaffs_readpage,
@@ -170,6 +175,10 @@ static struct file_operations yaffs_file_operations = {
        .mmap = generic_file_mmap,
        .flush = yaffs_file_flush,
        .fsync = yaffs_sync_object,
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+       .sendfile = generic_file_sendfile,
+#endif
+
 };
 
 static struct inode_operations yaffs_file_inode_operations = {
@@ -246,7 +255,11 @@ static int yaffs_readlink(struct dentry *dentry, char __user * buffer,
        return ret;
 }
 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13))
+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
+#else
 static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
+#endif
 {
        unsigned char *alias;
        int ret;
@@ -259,11 +272,19 @@ static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
        yaffs_GrossUnlock(dev);
 
        if (!alias)
-               return -ENOMEM;
+        {
+               ret = -ENOMEM;
+               goto out;
+        }
 
        ret = vfs_follow_link(nd, alias);
        kfree(alias);
+out:
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13))
+       return ERR_PTR (ret);
+#else
        return ret;
+#endif
 }
 
 struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,
@@ -296,6 +317,9 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry)
                                   dentry->d_name.name);
 
        obj = yaffs_GetEquivalentObject(obj);   /* in case it was a hardlink */
+       
+       /* Can't hold gross lock when calling yaffs_get_inode() */
+       yaffs_GrossUnlock(dev);
 
        if (obj) {
                T(YAFFS_TRACE_OS,
@@ -312,8 +336,6 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry)
                        /*dget(dentry); // try to solve directory bug */
                        d_add(dentry, inode);
 
-                       yaffs_GrossUnlock(dev);
-
                        /* return dentry; */
                        return NULL;
 #endif
@@ -323,7 +345,6 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry)
                T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_lookup not found\n"));
 
        }
-       yaffs_GrossUnlock(dev);
 
 /* added NCB for 2.5/6 compatability - forces add even if inode is
  * NULL which creates dentry hash */
@@ -401,6 +422,9 @@ static void yaffs_delete_inode(struct inode *inode)
                yaffs_DeleteFile(obj);
                yaffs_GrossUnlock(dev);
        }
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13))
+        truncate_inode_pages (&inode->i_data, 0);
+#endif
        clear_inode(inode);
 }
 
@@ -607,9 +631,10 @@ static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset,
        }
 
        T(YAFFS_TRACE_OS,
-         (KERN_DEBUG "yaffs_commit_write returning %d\n", nWritten));
+         (KERN_DEBUG "yaffs_commit_write returning %d\n",
+          nWritten == nBytes ? 0 : nWritten));
 
-       return nWritten;
+       return nWritten == nBytes ? 0 : nWritten;
 
 }
 
@@ -708,6 +733,7 @@ struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,
 
        /* NB Side effect: iget calls back to yaffs_read_inode(). */
        /* iget also increments the inode's i_count */
+       /* NB You can't be holding grossLock or deadlock will happen! */
 
        return inode;
 }
@@ -766,8 +792,7 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n,
 
        }
        yaffs_GrossUnlock(dev);
-
-       return nWritten != n ? -ENOSPC : nWritten;
+       return nWritten == 0 ? -ENOSPC : nWritten;
 }
 
 static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
@@ -816,6 +841,15 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
 
        curoffs = 1;
 
+       /* If the directory has changed since the open or last call to
+          readdir, rewind to after the 2 canned entries. */
+
+       if (f->f_version != inode->i_version) {
+               offset = 2;
+               f->f_pos = offset;
+               f->f_version = inode->i_version;
+       }
+
        list_for_each(i, &obj->variant.directoryVariant.children) {
                curoffs++;
                if (curoffs >= offset) {
@@ -923,6 +957,9 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,
                obj = NULL;     /* Do we ever get here? */
                break;
        }
+       
+       /* Can not call yaffs_get_inode() with gross lock held */
+       yaffs_GrossUnlock(dev);
 
        if (obj) {
                inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
@@ -937,8 +974,6 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,
                error = -ENOMEM;
        }
 
-       yaffs_GrossUnlock(dev);
-
        return error;
 }
 
@@ -983,15 +1018,15 @@ static int yaffs_unlink(struct inode *dir, struct dentry *dentry)
 
        retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name);
 
-       yaffs_GrossUnlock(dev);
-
        if (retVal == YAFFS_OK) {
                dentry->d_inode->i_nlink--;
+               dir->i_version++;
+               yaffs_GrossUnlock(dev);
                mark_inode_dirty(dentry->d_inode);
                return 0;
-       } else {
-               return -ENOTEMPTY;
        }
+       yaffs_GrossUnlock(dev);
+       return -ENOTEMPTY;
 }
 
 /*
@@ -1100,9 +1135,9 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,
 {
        yaffs_Device *dev;
        int retVal = YAFFS_FAIL;
-       int removed = 0;
        yaffs_Object *target;
 
+        T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_rename\n"));
        dev = yaffs_InodeToObject(old_dir)->myDev;
 
        yaffs_GrossLock(dev);
@@ -1111,15 +1146,21 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,
        target =
            yaffs_FindObjectByName(yaffs_InodeToObject(new_dir),
                                   new_dentry->d_name.name);
+       
+       
 
        if (target &&
            target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
            !list_empty(&target->variant.directoryVariant.children)) {
+           
+               T(YAFFS_TRACE_OS, (KERN_DEBUG "target is non-empty dir\n"));
+
                retVal = YAFFS_FAIL;
        } else {
 
                /* Now does unlinking internally using shadowing mechanism */
-
+               T(YAFFS_TRACE_OS, (KERN_DEBUG "calling yaffs_RenameObject\n"));
+               
                retVal =
                    yaffs_RenameObject(yaffs_InodeToObject(old_dir),
                                       old_dentry->d_name.name,
@@ -1130,7 +1171,7 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,
        yaffs_GrossUnlock(dev);
 
        if (retVal == YAFFS_OK) {
-               if (removed == YAFFS_OK) {
+               if(target) {
                        new_dentry->d_inode->i_nlink--;
                        mark_inode_dirty(new_dentry->d_inode);
                }
@@ -1213,8 +1254,9 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
 
 static void yaffs_read_inode(struct inode *inode)
 {
-       /* NB This is called as a side effect of other functions and
-        * thus gross locking should always be in place already.
+       /* NB This is called as a side effect of other functions, but
+        * we had to release the lock to prevent deadlocks, so 
+        * need to lock again.
         */
 
        yaffs_Object *obj;
@@ -1223,10 +1265,13 @@ static void yaffs_read_inode(struct inode *inode)
        T(YAFFS_TRACE_OS,
          (KERN_DEBUG "yaffs_read_inode for %d\n", (int)inode->i_ino));
 
+       yaffs_GrossLock(dev);
+       
        obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
 
        yaffs_FillInodeFromObject(inode, obj);
 
+       yaffs_GrossUnlock(dev);
 }
 
 static LIST_HEAD(yaffs_dev_list);
@@ -1236,19 +1281,29 @@ static void yaffs_put_super(struct super_block *sb)
        yaffs_Device *dev = yaffs_SuperToDevice(sb);
 
        yaffs_GrossLock(dev);
+       
+       yaffs_FlushEntireDeviceCache(dev);
+       
        if (dev->putSuperFunc) {
                dev->putSuperFunc(sb);
        }
+       
+       yaffs_CheckpointSave(dev);
        yaffs_Deinitialise(dev);
+       
        yaffs_GrossUnlock(dev);
 
        /* we assume this is protected by lock_kernel() in mount/umount */
        list_del(&dev->devList);
+       
+       if(dev->spareBuffer){
+               YFREE(dev->spareBuffer);
+               dev->spareBuffer = NULL;
+       }
 
        kfree(dev);
 }
 
-#ifdef CONFIG_YAFFS_YAFFS1
 
 static void yaffs_MTDPutSuper(struct super_block *sb)
 {
@@ -1262,7 +1317,6 @@ static void yaffs_MTDPutSuper(struct super_block *sb)
        put_mtd_device(mtd);
 }
 
-#endif
 
 static struct super_block *yaffs_internal_read_super(int yaffsVersion,
                                                     struct super_block *sb,
@@ -1337,6 +1391,15 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
        T(YAFFS_TRACE_OS, (" oobsize %d\n", mtd->oobsize));
        T(YAFFS_TRACE_OS, (" erasesize %d\n", mtd->erasesize));
        T(YAFFS_TRACE_OS, (" size %d\n", mtd->size));
+       
+#ifdef CONFIG_YAFFS_AUTO_YAFFS2
+
+       if (yaffsVersion == 1 && 
+           mtd->oobblock >= 2048) {
+           T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n"));
+           yaffsVersion = 2;
+       }       
+#endif
 
        if (yaffsVersion == 2) {
                /* Check for version 2 style functions */
@@ -1356,7 +1419,7 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
                if (mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
                    mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) {
                        T(YAFFS_TRACE_ALWAYS,
-                         ("yaffs: MTD device does not support have the "
+                         ("yaffs: MTD device does not have the "
                           "right page sizes\n"));
                        return NULL;
                }
@@ -1427,7 +1490,9 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
                dev->nBytesPerChunk = mtd->oobblock;
                dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
                nBlocks = mtd->size / mtd->erasesize;
-               dev->startBlock = 0;
+               dev->checkpointStartBlock = 0;
+               dev->checkpointEndBlock = 20;
+               dev->startBlock = dev->checkpointEndBlock + 1;
                dev->endBlock = nBlocks - 1;
        } else {
                dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
@@ -1444,6 +1509,10 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
        dev->useNANDECC = 1;
 #endif
 
+#ifdef CONFIG_YAFFS_DISABLE_WIDE_TNODES
+       dev->wideTnodesDisabled = 1;
+#endif
+
        /* we assume this is protected by lock_kernel() in mount/umount */
        list_add_tail(&dev->devList, &yaffs_dev_list);
 
@@ -1456,14 +1525,15 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
        T(YAFFS_TRACE_OS,
          ("yaffs_read_super: guts initialised %s\n",
           (err == YAFFS_OK) ? "OK" : "FAILED"));
+       
+       /* Release lock before yaffs_get_inode() */
+       yaffs_GrossUnlock(dev);
 
        /* Create root inode */
        if (err == YAFFS_OK)
                inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0,
                                        yaffs_Root(dev));
 
-       yaffs_GrossUnlock(dev);
-
        if (!inode)
                return NULL;
 
@@ -1486,13 +1556,12 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
        return sb;
 }
 
-#ifdef CONFIG_YAFFS_YAFFS1
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data,
                                         int silent)
 {
-       return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -1;
+       return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL;
 }
 
 static struct super_block *yaffs_read_super(struct file_system_type *fs,
@@ -1522,7 +1591,6 @@ static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super,
                      FS_REQUIRES_DEV);
 #endif
 
-#endif                         /* CONFIG_YAFFS_YAFFS1 */
 
 #ifdef CONFIG_YAFFS_YAFFS2
 
@@ -1530,7 +1598,7 @@ static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super,
 static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data,
                                          int silent)
 {
-       return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -1;
+       return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL;
 }
 
 static struct super_block *yaffs2_read_super(struct file_system_type *fs,
@@ -1645,6 +1713,118 @@ static int yaffs_proc_read(char *page,
        return buf - page < count ? buf - page : count;
 }
 
+/**
+ * Set the verbosity of the warnings and error messages.
+ *
+ */
+
+static struct {
+       char *mask_name;
+       unsigned mask_bitfield;
+} mask_flags[] = {
+       {"allocate", YAFFS_TRACE_ALLOCATE},
+       {"always", YAFFS_TRACE_ALWAYS},
+       {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS},
+       {"buffers", YAFFS_TRACE_BUFFERS},
+       {"bug", YAFFS_TRACE_BUG},
+       {"deletion", YAFFS_TRACE_DELETION},
+       {"erase", YAFFS_TRACE_ERASE},
+       {"error", YAFFS_TRACE_ERROR},
+       {"gc_detail", YAFFS_TRACE_GC_DETAIL},
+       {"gc", YAFFS_TRACE_GC},
+       {"mtd", YAFFS_TRACE_MTD},
+       {"nandaccess", YAFFS_TRACE_NANDACCESS},
+       {"os", YAFFS_TRACE_OS},
+       {"scan_debug", YAFFS_TRACE_SCAN_DEBUG},
+       {"scan", YAFFS_TRACE_SCAN},
+       {"tracing", YAFFS_TRACE_TRACING},
+       {"write", YAFFS_TRACE_WRITE},
+       {"all", 0xffffffff},
+       {"none", 0},
+       {NULL, 0},
+};
+
+static int yaffs_proc_write(struct file *file, const char *buf,
+                                        unsigned long count, void *data)
+{
+       unsigned rg = 0, mask_bitfield;
+       char *end, *mask_name;
+       int i;
+       int done = 0;
+       int add, len;
+       int pos = 0;
+
+       rg = yaffs_traceMask;
+
+       while (!done && (pos < count)) {
+               done = 1;
+               while ((pos < count) && isspace(buf[pos])) {
+                       pos++;
+               }
+
+               switch (buf[pos]) {
+               case '+':
+               case '-':
+               case '=':
+                       add = buf[pos];
+                       pos++;
+                       break;
+
+               default:
+                       add = ' ';
+                       break;
+               }
+               mask_name = NULL;
+               mask_bitfield = simple_strtoul(buf + pos, &end, 0);
+               if (end > buf + pos) {
+                       mask_name = "numeral";
+                       len = end - (buf + pos);
+                       done = 0;
+               } else {
+
+                       for (i = 0; mask_flags[i].mask_name != NULL; i++) {
+                               len = strlen(mask_flags[i].mask_name);
+                               if (strncmp(buf + pos, mask_flags[i].mask_name, len) == 0) {
+                                       mask_name = mask_flags[i].mask_name;
+                                       mask_bitfield = mask_flags[i].mask_bitfield;
+                                       done = 0;
+                                       break;
+                               }
+                       }
+               }
+
+               if (mask_name != NULL) {
+                       pos += len;
+                       done = 0;
+                       switch(add) {
+                       case '-':
+                               rg &= ~mask_bitfield;
+                               break;
+                       case '+':
+                               rg |= mask_bitfield;
+                               break;
+                       case '=':
+                               rg = mask_bitfield;
+                               break;
+                       default:
+                               rg |= mask_bitfield;
+                               break;
+                       }
+               }
+       }
+
+       yaffs_traceMask = rg;
+       if (rg & YAFFS_TRACE_ALWAYS) {
+               for (i = 0; mask_flags[i].mask_name != NULL; i++) {
+                       char flag;
+                       flag = ((rg & mask_flags[i].mask_bitfield) == mask_flags[i].mask_bitfield) ? '+' : '-';
+                       printk("%c%s\n", flag, mask_flags[i].mask_name);
+               }
+       }
+
+       return count;
+}
+
 /* Stuff to handle installation of file systems */
 struct file_system_to_install {
        struct file_system_type *fst;
@@ -1652,12 +1832,12 @@ struct file_system_to_install {
 };
 
 static struct file_system_to_install fs_to_install[] = {
-#ifdef CONFIG_YAFFS_YAFFS1
+//#ifdef CONFIG_YAFFS_YAFFS1
        {&yaffs_fs_type, 0},
-#endif
-#ifdef CONFIG_YAFFS_YAFFS2
+//#endif
+//#ifdef CONFIG_YAFFS_YAFFS2
        {&yaffs2_fs_type, 0},
-#endif
+//#endif
        {NULL, 0}
 };
 
@@ -1670,11 +1850,15 @@ static int __init init_yaffs_fs(void)
          ("yaffs " __DATE__ " " __TIME__ " Installing. \n"));
 
        /* Install the proc_fs entry */
-       my_proc_entry = create_proc_read_entry("yaffs",
+       my_proc_entry = create_proc_entry("yaffs",
                                               S_IRUGO | S_IFREG,
-                                              &proc_root,
-                                              yaffs_proc_read, NULL);
-       if (!my_proc_entry) {
+                                              &proc_root);
+
+       if (my_proc_entry) {
+               my_proc_entry->write_proc = yaffs_proc_write;
+               my_proc_entry->read_proc = yaffs_proc_read;
+               my_proc_entry->data = NULL;
+       } else {
                return -ENOMEM;
        }
 
@@ -1732,5 +1916,5 @@ module_init(init_yaffs_fs)
 module_exit(exit_yaffs_fs)
 
 MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system");
-MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002,2003,2004");
+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2006");
 MODULE_LICENSE("GPL");