Change locking to allow reentry during readdir
[yaffs2.git] / yaffs_fs.c
index a51dc20de633f900f25cf4ba5c8d993142956012..dfa94a2955d0bec53a9af4a3d2d31129e2f1b7c6 100644 (file)
@@ -32,7 +32,7 @@
  */
 
 const char *yaffs_fs_c_version =
-    "$Id: yaffs_fs.c,v 1.84 2009-10-14 00:01:56 charles Exp $";
+    "$Id: yaffs_fs.c,v 1.87 2009-11-11 02:11:13 charles Exp $";
 extern const char *yaffs_guts_c_version;
 
 #include <linux/version.h>
@@ -118,7 +118,7 @@ static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size)
 #include "yaffs_mtdif1.h"
 #include "yaffs_mtdif2.h"
 
-unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS;
+unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS;
 unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS;
 unsigned int yaffs_auto_checkpoint = 1;
 
@@ -365,6 +365,54 @@ static const struct super_operations yaffs_super_ops = {
        .write_super = yaffs_write_super,
 };
 
+/* YAFFS uses two locks per yaffs_Device.
+ * dirLock: r/w lock Must be held when accessing directory structure.
+ * grossLock: Lock when accessing yaffs internals.
+ *
+ * Locking rules:
+ *  If you're going to take dirLock then you must take if before
+ *  taking grossLock.
+ *  ie. Don't call yaffs_DirLockxxx()  while holding grossLock.
+ *
+ * Todo:
+ *   Investigate changing to mutexes etc and improve debugging.
+ */
+static void yaffs_DirLockInitialise(yaffs_Device *dev)
+{
+       init_rwsem(&dev->dirLock);
+}
+
+static void yaffs_DirLockRead(yaffs_Device *dev)
+{
+       T(YAFFS_TRACE_OS, ("yaffs locking dir read %p\n", current));
+       down_read(&dev->dirLock);
+       T(YAFFS_TRACE_OS, ("yaffs locked dir read %p\n", current));
+}
+
+static void yaffs_DirUnlockRead(yaffs_Device *dev)
+{
+       T(YAFFS_TRACE_OS, ("yaffs unlocking dir read %p\n", current));
+       up_read(&dev->dirLock);
+}
+
+static void yaffs_DirLockWrite(yaffs_Device *dev)
+{
+       T(YAFFS_TRACE_OS, ("yaffs locking dir write %p\n", current));
+       down_write(&dev->dirLock);
+       T(YAFFS_TRACE_OS, ("yaffs locked dir write %p\n", current));
+}
+
+static void yaffs_DirUnlockWrite(yaffs_Device *dev)
+{
+       T(YAFFS_TRACE_OS, ("yaffs unlocking dir write %p\n", current));
+       up_write(&dev->dirLock);
+}
+
+static void yaffs_GrossLockInitialise(yaffs_Device *dev)
+{
+       init_MUTEX(&dev->grossLock);
+}
+
 static void yaffs_GrossLock(yaffs_Device *dev)
 {
        T(YAFFS_TRACE_OS, ("yaffs locking %p\n", current));
@@ -378,6 +426,7 @@ static void yaffs_GrossUnlock(yaffs_Device *dev)
        up(&dev->grossLock);
 }
 
+
 static int yaffs_readlink(struct dentry *dentry, char __user *buffer,
                        int buflen)
 {
@@ -385,12 +434,14 @@ static int yaffs_readlink(struct dentry *dentry, char __user *buffer,
        int ret;
 
        yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
-
+       
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 
        if (!alias)
                return -ENOMEM;
@@ -410,11 +461,13 @@ static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
        int ret;
        yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 
        if (!alias) {
                ret = -ENOMEM;
@@ -450,6 +503,7 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry)
 
        yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        T(YAFFS_TRACE_OS,
@@ -463,6 +517,7 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry)
 
        /* Can't hold gross lock when calling yaffs_get_inode() */
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 
        if (obj) {
                T(YAFFS_TRACE_OS,
@@ -526,6 +581,7 @@ static void yaffs_clear_inode(struct inode *inode)
 
        if (obj) {
                dev = obj->myDev;
+               yaffs_DirLockRead(dev);
                yaffs_GrossLock(dev);
 
                /* Clear the association between the inode and
@@ -542,6 +598,7 @@ static void yaffs_clear_inode(struct inode *inode)
                yaffs_HandleDeferedFree(obj);
 
                yaffs_GrossUnlock(dev);
+               yaffs_DirUnlockRead(dev);
        }
 
 }
@@ -563,9 +620,11 @@ static void yaffs_delete_inode(struct inode *inode)
 
        if (obj) {
                dev = obj->myDev;
+               yaffs_DirLockWrite(dev);
                yaffs_GrossLock(dev);
                yaffs_DeleteObject(obj);
                yaffs_GrossUnlock(dev);
+               yaffs_DirUnlockWrite(dev);
        }
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13))
        truncate_inode_pages(&inode->i_data, 0);
@@ -587,11 +646,13 @@ static int yaffs_file_flush(struct file *file)
                ("yaffs_file_flush object %d (%s)\n", obj->objectId,
                obj->dirty ? "dirty" : "clean"));
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        yaffs_FlushFile(obj, 1,0);
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 
        return 0;
 }
@@ -624,6 +685,7 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg)
        pg_buf = kmap(pg);
        /* FIXME: Can kmap fail? */
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        ret = yaffs_ReadDataFromFile(obj, pg_buf,
@@ -631,6 +693,7 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg)
                                PAGE_CACHE_SIZE);
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 
        if (ret >= 0)
                ret = 0;
@@ -709,6 +772,7 @@ static int yaffs_writepage(struct page *page)
        buffer = kmap(page);
 
        obj = yaffs_InodeToObject(inode);
+       yaffs_DirLockRead(obj->myDev);
        yaffs_GrossLock(obj->myDev);
 
        T(YAFFS_TRACE_OS,
@@ -726,6 +790,7 @@ static int yaffs_writepage(struct page *page)
                (int)obj->variant.fileVariant.fileSize, (int)inode->i_size));
 
        yaffs_GrossUnlock(obj->myDev);
+       yaffs_DirUnlockRead(obj->myDev);
 
        kunmap(page);
        SetPageUptodate(page);
@@ -1041,6 +1106,7 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n,
 
        dev = obj->myDev;
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        inode = f->f_dentry->d_inode;
@@ -1080,6 +1146,8 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n,
 
        }
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
+
        return (nWritten == 0) && (n > 0) ? -ENOSPC : nWritten;
 }
 
@@ -1099,29 +1167,19 @@ static ssize_t yaffs_hold_space(struct file *f)
 
        dev = obj->myDev;
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        nFreeChunks = yaffs_GetNumberOfFreeChunks(dev);
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 
        return (nFreeChunks > 20) ? 1 : 0;
 }
 
 static void yaffs_release_space(struct file *f)
 {
-       yaffs_Object *obj;
-       yaffs_Device *dev;
-
-
-       obj = yaffs_DentryToObject(f->f_dentry);
-
-       dev = obj->myDev;
-
-       yaffs_GrossLock(dev);
-
-
-       yaffs_GrossUnlock(dev);
 }
 
 static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
@@ -1138,6 +1196,7 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
        obj = yaffs_DentryToObject(f->f_dentry);
        dev = obj->myDev;
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        offset = f->f_pos;
@@ -1148,8 +1207,14 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
                T(YAFFS_TRACE_OS,
                        ("yaffs_readdir: entry . ino %d \n",
                        (int)inode->i_ino));
+
+               yaffs_GrossUnlock(dev);
+               
                if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0)
                        goto out;
+
+               yaffs_GrossLock(dev);
+
                offset++;
                f->f_pos++;
        }
@@ -1157,9 +1222,15 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
                T(YAFFS_TRACE_OS,
                        ("yaffs_readdir: entry .. ino %d \n",
                        (int)f->f_dentry->d_parent->d_inode->i_ino));
+
+               yaffs_GrossUnlock(dev);
+
                if (filldir(dirent, "..", 2, offset,
                        f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
                        goto out;
+
+               yaffs_GrossLock(dev);
+
                offset++;
                f->f_pos++;
        }
@@ -1186,22 +1257,26 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
                          ("yaffs_readdir: %s inode %d\n", name,
                           yaffs_GetObjectInode(l)));
 
+                       yaffs_GrossUnlock(dev);
+
                        if (filldir(dirent,
                                        name,
                                        strlen(name),
                                        offset,
                                        yaffs_GetObjectInode(l),
                                        yaffs_GetObjectType(l)) < 0)
-                               goto up_and_out;
+                               goto out;
+
+                       yaffs_GrossLock(dev);
 
                        offset++;
                        f->f_pos++;
                }
        }
 
-up_and_out:
-out:
        yaffs_GrossUnlock(dev);
+out:
+       yaffs_DirUnlockRead(dev);
 
        return 0;
 }
@@ -1254,6 +1329,7 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,
 
        dev = parent->myDev;
 
+       yaffs_DirLockWrite(dev);
        yaffs_GrossLock(dev);
 
        switch (mode & S_IFMT) {
@@ -1287,6 +1363,7 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,
 
        /* Can not call yaffs_get_inode() with gross lock held */
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockWrite(dev);
 
        if (obj) {
                inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
@@ -1336,6 +1413,7 @@ static int yaffs_unlink(struct inode *dir, struct dentry *dentry)
 
        dev = yaffs_InodeToObject(dir)->myDev;
 
+       yaffs_DirLockWrite(dev); 
        yaffs_GrossLock(dev);
 
        retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name);
@@ -1343,12 +1421,17 @@ static int yaffs_unlink(struct inode *dir, struct dentry *dentry)
        if (retVal == YAFFS_OK) {
                dentry->d_inode->i_nlink--;
                dir->i_version++;
-               yaffs_GrossUnlock(dev);
+       }
+
+       yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockWrite(dev);
+
+       if( retVal == YAFFS_OK){
                mark_inode_dirty(dentry->d_inode);
                update_dir_time(dir);
                return 0;
        }
-       yaffs_GrossUnlock(dev);
+
        return -ENOTEMPTY;
 }
 
@@ -1368,6 +1451,7 @@ static int yaffs_link(struct dentry *old_dentry, struct inode *dir,
        obj = yaffs_InodeToObject(inode);
        dev = obj->myDev;
 
+       yaffs_DirLockWrite(dev);
        yaffs_GrossLock(dev);
 
        if (!S_ISDIR(inode->i_mode))            /* Don't link directories */
@@ -1385,6 +1469,7 @@ static int yaffs_link(struct dentry *old_dentry, struct inode *dir,
        }
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockWrite(dev);
 
        if (link){
                update_dir_time(dir);
@@ -1405,10 +1490,12 @@ static int yaffs_symlink(struct inode *dir, struct dentry *dentry,
        T(YAFFS_TRACE_OS, ("yaffs_symlink\n"));
 
        dev = yaffs_InodeToObject(dir)->myDev;
+       yaffs_DirLockWrite(dev);
        yaffs_GrossLock(dev);
        obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name,
                                S_IFLNK | S_IRWXUGO, uid, gid, symname);
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockWrite(dev);
 
        if (obj) {
                struct inode *inode;
@@ -1437,9 +1524,11 @@ static int yaffs_sync_object(struct file *file, struct dentry *dentry,
        dev = obj->myDev;
 
        T(YAFFS_TRACE_OS, ("yaffs_sync_object\n"));
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
        yaffs_FlushFile(obj, 1, datasync);
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
        return 0;
 }
 
@@ -1458,6 +1547,7 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,
        T(YAFFS_TRACE_OS, ("yaffs_rename\n"));
        dev = yaffs_InodeToObject(old_dir)->myDev;
 
+       yaffs_DirLockWrite(dev);
        yaffs_GrossLock(dev);
 
        /* Check if the target is an existing directory that is not empty. */
@@ -1482,6 +1572,7 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,
                                new_dentry->d_name.name);
        }
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockWrite(dev);
 
        if (retVal == YAFFS_OK) {
                if (target) {
@@ -1511,6 +1602,7 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
        error = inode_change_ok(inode, attr);
        if (error == 0) {
                dev = yaffs_InodeToObject(inode)->myDev;
+               yaffs_DirLockRead(dev);
                yaffs_GrossLock(dev);
                if (yaffs_SetAttributes(yaffs_InodeToObject(inode), attr) ==
                                YAFFS_OK) {
@@ -1519,6 +1611,7 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
                        error = -EPERM;
                }
                yaffs_GrossUnlock(dev);
+               yaffs_DirUnlockRead(dev);
                if (!error)
                        error = inode_setattr(inode, attr);
        }
@@ -1542,6 +1635,7 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
 
        T(YAFFS_TRACE_OS, ("yaffs_statfs\n"));
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        buf->f_type = YAFFS_MAGIC;
@@ -1592,6 +1686,7 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
        buf->f_bavail = buf->f_bfree;
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
        return 0;
 }
 
@@ -1618,6 +1713,7 @@ static int yaffs_do_sync_fs(struct super_block *sb)
        T(YAFFS_TRACE_OS, ("yaffs_do_sync_fs\n"));
 
        if (sb->s_dirt) {
+               yaffs_DirLockRead(dev);
                yaffs_GrossLock(dev);
 
                if (dev) {
@@ -1627,6 +1723,7 @@ static int yaffs_do_sync_fs(struct super_block *sb)
                }
 
                yaffs_GrossUnlock(dev);
+               yaffs_DirUnlockRead(dev);
 
                sb->s_dirt = 0;
        }
@@ -1686,6 +1783,7 @@ static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino)
         * need to lock again.
         */
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
@@ -1693,6 +1791,7 @@ static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino)
        yaffs_FillInodeFromObject(inode, obj);
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 
        unlock_new_inode(inode);
        return inode;
@@ -1713,6 +1812,7 @@ static void yaffs_read_inode(struct inode *inode)
        T(YAFFS_TRACE_OS,
                ("yaffs_read_inode for %d\n", (int)inode->i_ino));
 
+       yaffs_DirLockRead(dev);
        yaffs_GrossLock(dev);
 
        obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
@@ -1720,6 +1820,7 @@ static void yaffs_read_inode(struct inode *inode)
        yaffs_FillInodeFromObject(inode, obj);
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockRead(dev);
 }
 
 #endif
@@ -1736,7 +1837,7 @@ static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
 
                T(YAFFS_TRACE_OS,
                        ("yaffs_remount_fs: %s: RO\n", dev->name));
-
+               yaffs_DirLockWrite(dev);
                yaffs_GrossLock(dev);
 
                yaffs_FlushEntireDeviceCache(dev);
@@ -1747,6 +1848,7 @@ static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
                        mtd->sync(mtd);
 
                yaffs_GrossUnlock(dev);
+               yaffs_DirUnlockWrite(dev);
        } else {
                T(YAFFS_TRACE_OS,
                        ("yaffs_remount_fs: %s: RW\n", dev->name));
@@ -1762,6 +1864,7 @@ static void yaffs_put_super(struct super_block *sb)
 
        T(YAFFS_TRACE_OS, ("yaffs_put_super\n"));
 
+       yaffs_DirLockWrite(dev);
        yaffs_GrossLock(dev);
 
        yaffs_FlushEntireDeviceCache(dev);
@@ -1774,6 +1877,7 @@ static void yaffs_put_super(struct super_block *sb)
        yaffs_Deinitialise(dev);
 
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockWrite(dev);
 
        /* we assume this is protected by lock_kernel() in mount/umount */
        ylist_del(&dev->devList);
@@ -1814,9 +1918,13 @@ typedef struct {
        int no_cache;
        int tags_ecc_on;
        int tags_ecc_overridden;
+       int lazy_loading_enabled;
+       int lazy_loading_overridden;
+       int empty_lost_and_found;
+       int empty_lost_and_found_overridden;
 } yaffs_options;
 
-#define MAX_OPT_LEN 20
+#define MAX_OPT_LEN 30
 static int yaffs_parse_options(yaffs_options *options, const char *options_str)
 {
        char cur_opt[MAX_OPT_LEN + 1];
@@ -1848,6 +1956,18 @@ static int yaffs_parse_options(yaffs_options *options, const char *options_str)
                } else if (!strcmp(cur_opt, "tags-ecc-on")){
                        options->tags_ecc_on = 1;
                        options->tags_ecc_overridden = 1;
+               } else if (!strcmp(cur_opt, "lazy-loading-off")){
+                       options->lazy_loading_enabled = 0;
+                       options->lazy_loading_overridden=1;
+               } else if (!strcmp(cur_opt, "lazy-loading-on")){
+                       options->lazy_loading_enabled = 1;
+                       options->lazy_loading_overridden = 1;
+               } else if (!strcmp(cur_opt, "empty-lost-and-found-off")){
+                       options->empty_lost_and_found = 0;
+                       options->empty_lost_and_found_overridden=1;
+               } else if (!strcmp(cur_opt, "empty-lost-and-found-on")){
+                       options->empty_lost_and_found = 1;
+                       options->empty_lost_and_found_overridden=1;
                } else if (!strcmp(cur_opt, "no-cache"))
                        options->no_cache = 1;
                else if (!strcmp(cur_opt, "no-checkpoint-read"))
@@ -2064,12 +2184,24 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
        dev->nShortOpCaches = (options.no_cache) ? 0 : 10;
        dev->inbandTags = options.inband_tags;
 
+#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD
+       dev->disableLazyLoad = 1;
+#endif
+       if(options.lazy_loading_overridden)
+               dev->disableLazyLoad = !options.lazy_loading_enabled;
+
 #ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC
        dev->noTagsECC = 1;
 #endif
        if(options.tags_ecc_overridden)
                dev->noTagsECC = !options.tags_ecc_on;
 
+#ifdef CONFIG_YAFFS_EMPTY_LOST_AND_FOUND
+       dev->emptyLostAndFound = 1;
+#endif
+       if(options.empty_lost_and_found_overridden)
+               dev->emptyLostAndFound = options.empty_lost_and_found;
+
        /* ... and the functions. */
        if (yaffsVersion == 2) {
                dev->writeChunkWithTagsToNAND =
@@ -2129,9 +2261,11 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
 
        /* we assume this is protected by lock_kernel() in mount/umount */
        ylist_add_tail(&dev->devList, &yaffs_dev_list);
+       
+       yaffs_DirLockInitialise(dev);
+       yaffs_GrossLockInitialise(dev);
 
-       init_MUTEX(&dev->grossLock);
-
+       yaffs_DirLockWrite(dev);
        yaffs_GrossLock(dev);
 
        err = yaffs_GutsInitialise(dev);
@@ -2142,6 +2276,7 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
 
        /* Release lock before yaffs_get_inode() */
        yaffs_GrossUnlock(dev);
+       yaffs_DirUnlockWrite(dev);
 
        /* Create root inode */
        if (err == YAFFS_OK)
@@ -2270,7 +2405,7 @@ static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super,
 
 static struct proc_dir_entry *my_proc_entry;
 
-static char *yaffs_dump_dev(char *buf, yaffs_Device * dev)
+static char *yaffs_dump_dev_part0(char *buf, yaffs_Device * dev)
 {
        buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock);
        buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock);
@@ -2305,10 +2440,19 @@ static char *yaffs_dump_dev(char *buf, yaffs_Device * dev)
        buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles);
        buf +=
            sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions);
+
+       return buf;
+}
+
+
+static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev)
+{
        buf += sprintf(buf, "useNANDECC......... %d\n", dev->useNANDECC);
        buf += sprintf(buf, "noTagsECC.......... %d\n", dev->noTagsECC);
        buf += sprintf(buf, "isYaffs2........... %d\n", dev->isYaffs2);
        buf += sprintf(buf, "inbandTags......... %d\n", dev->inbandTags);
+       buf += sprintf(buf, "emptyLostAndFound.. %d\n", dev->emptyLostAndFound);
+       buf += sprintf(buf, "disableLazyLoad.... %d\n", dev->disableLazyLoad);
 
        return buf;
 }
@@ -2331,27 +2475,35 @@ static int yaffs_proc_read(char *page,
        *(int *)start = 1;
 
        /* Print header first */
-       if (step == 0) {
+       if (step == 0)
                buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__
                               "\n%s\n%s\n", yaffs_fs_c_version,
                               yaffs_guts_c_version);
-       }
-
-       /* hold lock_kernel while traversing yaffs_dev_list */
-       lock_kernel();
-
-       /* Locate and print the Nth entry.  Order N-squared but N is small. */
-       ylist_for_each(item, &yaffs_dev_list) {
-               yaffs_Device *dev = ylist_entry(item, yaffs_Device, devList);
-               if (n < step) {
-                       n++;
-                       continue;
+       else if (step == 1)
+               buf += sprintf(buf,"\n");
+       else {
+               step-=2;
+               
+               /* hold lock_kernel while traversing yaffs_dev_list */
+               lock_kernel();
+
+               /* Locate and print the Nth entry.  Order N-squared but N is small. */
+               ylist_for_each(item, &yaffs_dev_list) {
+                       yaffs_Device *dev = ylist_entry(item, yaffs_Device, devList);
+                       if (n < (step & ~1)) {
+                               n+=2;
+                               continue;
+                       }
+                       if((step & 1)==0){
+                               buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name);
+                               buf = yaffs_dump_dev_part0(buf, dev);
+                       } else
+                               buf = yaffs_dump_dev_part1(buf, dev);
+                       
+                       break;
                }
-               buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name);
-               buf = yaffs_dump_dev(buf, dev);
-               break;
+               unlock_kernel();
        }
-       unlock_kernel();
 
        return buf - page < count ? buf - page : count;
 }
@@ -2396,7 +2548,7 @@ static struct {
 };
 
 #define MAX_MASK_NAME_LENGTH 40
-static int yaffs_proc_write(struct file *file, const char *buf,
+static int yaffs_proc_write_trace_options(struct file *file, const char *buf,
                                         unsigned long count, void *data)
 {
        unsigned rg = 0, mask_bitfield;
@@ -2488,6 +2640,13 @@ static int yaffs_proc_write(struct file *file, const char *buf,
        return count;
 }
 
+
+static int yaffs_proc_write(struct file *file, const char *buf,
+                                        unsigned long count, void *data)
+{
+        return yaffs_proc_write_trace_options(file, buf, count, data);
+}
+
 /* Stuff to handle installation of file systems */
 struct file_system_to_install {
        struct file_system_type *fst;