*/
const char *yaffs_fs_c_version =
- "$Id: yaffs_fs.c,v 1.85 2009-10-15 00:45:46 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>
#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;
.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));
up(&dev->grossLock);
}
+
static int yaffs_readlink(struct dentry *dentry, char __user *buffer,
int buflen)
{
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;
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;
yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
T(YAFFS_TRACE_OS,
/* Can't hold gross lock when calling yaffs_get_inode() */
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
if (obj) {
T(YAFFS_TRACE_OS,
if (obj) {
dev = obj->myDev;
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
/* Clear the association between the inode and
yaffs_HandleDeferedFree(obj);
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
}
}
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);
("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;
}
pg_buf = kmap(pg);
/* FIXME: Can kmap fail? */
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
ret = yaffs_ReadDataFromFile(obj, pg_buf,
PAGE_CACHE_SIZE);
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
if (ret >= 0)
ret = 0;
buffer = kmap(page);
obj = yaffs_InodeToObject(inode);
+ yaffs_DirLockRead(obj->myDev);
yaffs_GrossLock(obj->myDev);
T(YAFFS_TRACE_OS,
(int)obj->variant.fileVariant.fileSize, (int)inode->i_size));
yaffs_GrossUnlock(obj->myDev);
+ yaffs_DirUnlockRead(obj->myDev);
kunmap(page);
SetPageUptodate(page);
dev = obj->myDev;
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
inode = f->f_dentry->d_inode;
}
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
+
return (nWritten == 0) && (n > 0) ? -ENOSPC : nWritten;
}
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)
obj = yaffs_DentryToObject(f->f_dentry);
dev = obj->myDev;
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
offset = f->f_pos;
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++;
}
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++;
}
("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;
}
dev = parent->myDev;
+ yaffs_DirLockWrite(dev);
yaffs_GrossLock(dev);
switch (mode & S_IFMT) {
/* 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);
dev = yaffs_InodeToObject(dir)->myDev;
+ yaffs_DirLockWrite(dev);
yaffs_GrossLock(dev);
retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name);
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;
}
obj = yaffs_InodeToObject(inode);
dev = obj->myDev;
+ yaffs_DirLockWrite(dev);
yaffs_GrossLock(dev);
if (!S_ISDIR(inode->i_mode)) /* Don't link directories */
}
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockWrite(dev);
if (link){
update_dir_time(dir);
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;
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;
}
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. */
new_dentry->d_name.name);
}
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockWrite(dev);
if (retVal == YAFFS_OK) {
if (target) {
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) {
error = -EPERM;
}
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
if (!error)
error = inode_setattr(inode, attr);
}
T(YAFFS_TRACE_OS, ("yaffs_statfs\n"));
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
buf->f_type = YAFFS_MAGIC;
buf->f_bavail = buf->f_bfree;
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
return 0;
}
T(YAFFS_TRACE_OS, ("yaffs_do_sync_fs\n"));
if (sb->s_dirt) {
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
if (dev) {
}
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
sb->s_dirt = 0;
}
* need to lock again.
*/
+ yaffs_DirLockRead(dev);
yaffs_GrossLock(dev);
obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
yaffs_FillInodeFromObject(inode, obj);
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
unlock_new_inode(inode);
return 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);
yaffs_FillInodeFromObject(inode, obj);
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockRead(dev);
}
#endif
T(YAFFS_TRACE_OS,
("yaffs_remount_fs: %s: RO\n", dev->name));
-
+ yaffs_DirLockWrite(dev);
yaffs_GrossLock(dev);
yaffs_FlushEntireDeviceCache(dev);
mtd->sync(mtd);
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockWrite(dev);
} else {
T(YAFFS_TRACE_OS,
("yaffs_remount_fs: %s: RW\n", dev->name));
T(YAFFS_TRACE_OS, ("yaffs_put_super\n"));
+ yaffs_DirLockWrite(dev);
yaffs_GrossLock(dev);
yaffs_FlushEntireDeviceCache(dev);
yaffs_Deinitialise(dev);
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockWrite(dev);
/* we assume this is protected by lock_kernel() in mount/umount */
ylist_del(&dev->devList);
int no_cache;
int tags_ecc_on;
int tags_ecc_overridden;
- int lazy_load_enabled;
- int lazy_load_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];
} else if (!strcmp(cur_opt, "tags-ecc-on")){
options->tags_ecc_on = 1;
options->tags_ecc_overridden = 1;
- } else if (!strcmp(cur_opt, "lazy-load-off")){
- options->lazy_load_enabled = 0;
- options->lazy_load_overridden=1;
- } else if (!strcmp(cur_opt, "lazy-load-on")){
- options->lazy_load_enabled = 1;
- options->lazy_load_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"))
#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD
dev->disableLazyLoad = 1;
#endif
- if(options.lazy_load_overridden)
- dev->disableLazyLoad = !options.lazy_load_enabled;
+ if(options.lazy_loading_overridden)
+ dev->disableLazyLoad = !options.lazy_loading_enabled;
#ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC
dev->noTagsECC = 1;
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 =
/* 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);
/* Release lock before yaffs_get_inode() */
yaffs_GrossUnlock(dev);
+ yaffs_DirUnlockWrite(dev);
/* Create root inode */
if (err == YAFFS_OK)
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);
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;
}
*(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;
}
};
#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;
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;