*/
const char *yaffs_fs_c_version =
- "$Id: yaffs_fs.c,v 1.83 2009-09-23 23:24:55 charles Exp $";
+ "$Id: yaffs_fs.c,v 1.99 2010-03-15 06:07:44 charles Exp $";
extern const char *yaffs_guts_c_version;
#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10))
+#define YAFFS_COMPILE_BACKGROUND
+#endif
+
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))
#include <linux/config.h>
#endif
#include <linux/string.h>
#include <linux/ctype.h>
+#ifdef YAFFS_COMPILE_BACKGROUND
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#endif
+
+
#include "asm/div64.h"
+
+#define LOCK_TRACE 0
+
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
#include <linux/statfs.h> /* Added NCB 15-8-2003 */
#include <linux/uaccess.h>
#include "yportenv.h"
+#include "yaffs_trace.h"
#include "yaffs_guts.h"
+#include "yaffs_linux.h"
+
#include <linux/mtd/mtd.h>
#include "yaffs_mtdif.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;
};
#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
+static void zero_user_segment(struct page *page, unsigned start, unsigned end)
+{
+ void * kaddr = kmap_atomic(page, KM_USER0);
+ memset(kaddr + start, 0, end - start);
+ kunmap_atomic(kaddr, KM_USER0);
+ flush_dcache_page(page);
+}
+#endif
+
+
static const struct inode_operations yaffs_file_inode_operations = {
.setattr = yaffs_setattr,
};
.sync_fs = yaffs_sync_fs,
.write_super = yaffs_write_super,
};
-
+
static void yaffs_GrossLock(yaffs_Device *dev)
{
- T(YAFFS_TRACE_OS, ("yaffs locking %p\n", current));
- down(&dev->grossLock);
- T(YAFFS_TRACE_OS, ("yaffs locked %p\n", current));
+ T(LOCK_TRACE && YAFFS_TRACE_OS, ("yaffs locking %p\n", current));
+ down(&(yaffs_DeviceToContext(dev)->grossLock));
+ T(LOCK_TRACE && YAFFS_TRACE_OS, ("yaffs locked %p\n", current));
}
static void yaffs_GrossUnlock(yaffs_Device *dev)
{
- T(YAFFS_TRACE_OS, ("yaffs unlocking %p\n", current));
- up(&dev->grossLock);
+ T(LOCK_TRACE && YAFFS_TRACE_OS, ("yaffs unlocking %p\n", current));
+ up(&(yaffs_DeviceToContext(dev)->grossLock));
+}
+
+
+/*-----------------------------------------------------------------*/
+/* Directory search context allows us to unlock access to yaffs during
+ * filldir without causing problems with the directory being modified.
+ * This is similar to the tried and tested mechanism used in yaffs direct.
+ *
+ * A search context iterates along a doubly linked list of siblings in the
+ * directory. If the iterating object is deleted then this would corrupt
+ * the list iteration, likely causing a crash. The search context avoids
+ * this by using the removeObjectCallback to move the search context to the
+ * next object before the object is deleted.
+ *
+ * Many readdirs (and thus seach conexts) may be alive simulateously so
+ * each yaffs_Device has a list of these.
+ *
+ * A seach context lives for the duration of a readdir.
+ *
+ * All these functions must be called while yaffs is locked.
+ */
+
+struct yaffs_SearchContext {
+ yaffs_Device *dev;
+ yaffs_Object *dirObj;
+ yaffs_Object *nextReturn;
+ struct ylist_head others;
+};
+
+/*
+ * yaffs_NewSearch() creates a new search context, initialises it and
+ * adds it to the device's search context list.
+ *
+ * Called at start of readdir.
+ */
+static struct yaffs_SearchContext * yaffs_NewSearch(yaffs_Object *dir)
+{
+ yaffs_Device *dev = dir->myDev;
+ struct yaffs_SearchContext *sc = YMALLOC(sizeof(struct yaffs_SearchContext));
+ if(sc){
+ sc->dirObj = dir;
+ sc->dev = dev;
+ if( ylist_empty(&sc->dirObj->variant.directoryVariant.children))
+ sc->nextReturn = NULL;
+ else
+ sc->nextReturn = ylist_entry(
+ dir->variant.directoryVariant.children.next,
+ yaffs_Object,siblings);
+ YINIT_LIST_HEAD(&sc->others);
+ ylist_add(&sc->others,&(yaffs_DeviceToContext(dev)->searchContexts));
+ }
+ return sc;
+}
+
+/*
+ * yaffs_EndSearch() disposes of a search context and cleans up.
+ */
+static void yaffs_EndSearch(struct yaffs_SearchContext * sc)
+{
+ if(sc){
+ ylist_del(&sc->others);
+ YFREE(sc);
+ }
+}
+
+/*
+ * yaffs_SearchAdvance() moves a search context to the next object.
+ * Called when the search iterates or when an object removal causes
+ * the search context to be moved to the next object.
+ */
+static void yaffs_SearchAdvance(struct yaffs_SearchContext *sc)
+{
+ if(!sc)
+ return;
+
+ if( sc->nextReturn == NULL ||
+ ylist_empty(&sc->dirObj->variant.directoryVariant.children))
+ sc->nextReturn = NULL;
+ else {
+ struct ylist_head *next = sc->nextReturn->siblings.next;
+
+ if( next == &sc->dirObj->variant.directoryVariant.children)
+ sc->nextReturn = NULL; /* end of list */
+ else
+ sc->nextReturn = ylist_entry(next,yaffs_Object,siblings);
+ }
}
+/*
+ * yaffs_RemoveObjectCallback() is called when an object is unlinked.
+ * We check open search contexts and advance any which are currently
+ * on the object being iterated.
+ */
+static void yaffs_RemoveObjectCallback(yaffs_Object *obj)
+{
+
+ struct ylist_head *i;
+ struct yaffs_SearchContext *sc;
+ struct ylist_head *search_contexts = &(yaffs_DeviceToContext(obj->myDev)->searchContexts);
+
+
+ /* Iterate through the directory search contexts.
+ * If any are currently on the object being removed, then advance
+ * the search context to the next object to prevent a hanging pointer.
+ */
+ ylist_for_each(i, search_contexts) {
+ if (i) {
+ sc = ylist_entry(i, struct yaffs_SearchContext,others);
+ if(sc->nextReturn == obj)
+ yaffs_SearchAdvance(sc);
+ }
+ }
+
+}
+
+
+/*-----------------------------------------------------------------*/
+
static int yaffs_readlink(struct dentry *dentry, char __user *buffer,
int buflen)
{
yaffs_GrossLock(dev);
- yaffs_FlushFile(obj, 1,0);
+ yaffs_FlushFile(obj, 1, 0);
yaffs_GrossUnlock(dev);
yaffs_Device *dev;
- T(YAFFS_TRACE_OS, ("yaffs_readpage at %08x, size %08x\n",
+ T(YAFFS_TRACE_OS, ("yaffs_readpage_nolock at %08x, size %08x\n",
(unsigned)(pg->index << PAGE_CACHE_SHIFT),
(unsigned)PAGE_CACHE_SIZE));
flush_dcache_page(pg);
kunmap(pg);
- T(YAFFS_TRACE_OS, ("yaffs_readpage done\n"));
+ T(YAFFS_TRACE_OS, ("yaffs_readpage_nolock done\n"));
return ret;
}
static int yaffs_readpage(struct file *f, struct page *pg)
{
- return yaffs_readpage_unlock(f, pg);
+ int ret;
+
+ T(YAFFS_TRACE_OS, ("yaffs_readpage\n"));
+ ret=yaffs_readpage_unlock(f, pg);
+ T(YAFFS_TRACE_OS, ("yaffs_readpage done\n"));
+ return ret;
}
/* writepage inspired by/stolen from smbfs */
#endif
{
struct address_space *mapping = page->mapping;
- loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT;
struct inode *inode;
unsigned long end_index;
char *buffer;
yaffs_Object *obj;
int nWritten = 0;
unsigned nBytes;
+ loff_t i_size;
if (!mapping)
BUG();
inode = mapping->host;
if (!inode)
BUG();
+ i_size = i_size_read(inode);
- if (offset > inode->i_size) {
- T(YAFFS_TRACE_OS,
- ("yaffs_writepage at %08x, inode size = %08x!!!\n",
- (unsigned)(page->index << PAGE_CACHE_SHIFT),
- (unsigned)inode->i_size));
- T(YAFFS_TRACE_OS,
- (" -> don't care!!\n"));
- unlock_page(page);
- return 0;
- }
+ end_index = i_size >> PAGE_CACHE_SHIFT;
- end_index = inode->i_size >> PAGE_CACHE_SHIFT;
-
- /* easy case */
- if (page->index < end_index)
+ if(page->index < end_index)
nBytes = PAGE_CACHE_SIZE;
- else
- nBytes = inode->i_size & (PAGE_CACHE_SIZE - 1);
+ else {
+ nBytes = i_size & (PAGE_CACHE_SIZE -1);
+
+ if (page->index > end_index || !nBytes) {
+ T(YAFFS_TRACE_OS,
+ ("yaffs_writepage at %08x, inode size = %08x!!!\n",
+ (unsigned)(page->index << PAGE_CACHE_SHIFT),
+ (unsigned)inode->i_size));
+ T(YAFFS_TRACE_OS,
+ (" -> don't care!!\n"));
+
+ zero_user_segment(page,0,PAGE_CACHE_SIZE);
+ set_page_writeback(page);
+ unlock_page(page);
+ end_page_writeback(page);
+ return 0;
+ }
+ }
+
+ if(nBytes != PAGE_CACHE_SIZE)
+ zero_user_segment(page,nBytes,PAGE_CACHE_SIZE);
get_page(page);
yaffs_GrossUnlock(obj->myDev);
kunmap(page);
- SetPageUptodate(page);
- UnlockPage(page);
+ set_page_writeback(page);
+ unlock_page(page);
+ end_page_writeback(page);
put_page(page);
return (nWritten == nBytes) ? 0 : -ENOSPC;
{
struct page *pg = NULL;
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
- uint32_t offset = pos & (PAGE_CACHE_SIZE - 1);
- uint32_t to = offset + len;
int ret = 0;
int space_held = 0;
- T(YAFFS_TRACE_OS, ("start yaffs_write_begin\n"));
/* Get a page */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
pg = grab_cache_page_write_begin(mapping, index, flags);
ret = -ENOMEM;
goto out;
}
+ T(YAFFS_TRACE_OS, ("start yaffs_write_begin index %d(%x) uptodate %d\n",(int)index,(int)index,Page_Uptodate(pg) ? 1 : 0));
+
/* Get fs space */
space_held = yaffs_hold_space(filp);
/* Update page if required */
- if (!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE))
+ if (!Page_Uptodate(pg))
ret = yaffs_readpage_nolock(filp, pg);
if (ret)
{
T(YAFFS_TRACE_OS, ("yaffs_prepair_write\n"));
- if (!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE))
+ if (!Page_Uptodate(pg))
return yaffs_readpage_nolock(f, pg);
return 0;
}
("yaffs_write_end not same size ret %d copied %d\n",
ret, copied));
SetPageError(pg);
- ClearPageUptodate(pg);
} else {
- SetPageUptodate(pg);
+ /* Nothing */
}
kunmap(pg);
("yaffs_commit_write not same size nWritten %d nBytes %d\n",
nWritten, nBytes));
SetPageError(pg);
- ClearPageUptodate(pg);
} else {
- SetPageUptodate(pg);
+ /* Nothing */
}
kunmap(pg);
("yaffs_file_write: hey obj is null!\n"));
else
T(YAFFS_TRACE_OS,
- ("yaffs_file_write about to write writing %zu bytes"
- "to object %d at %d\n",
- n, obj->objectId, ipos));
+ ("yaffs_file_write about to write writing %u(%x) bytes"
+ "to object %d at %d(%x)\n",
+ (unsigned) n, (unsigned) n, obj->objectId, ipos,ipos));
nWritten = yaffs_WriteDataToFile(obj, buf, ipos, n, 0);
T(YAFFS_TRACE_OS,
- ("yaffs_file_write writing %zu bytes, %d written at %d\n",
- n, nWritten, ipos));
+ ("yaffs_file_write: %d(%x) bytes written\n",
+ (unsigned )n,(unsigned)n));
if (nWritten > 0) {
ipos += nWritten;
{
yaffs_Object *obj;
yaffs_Device *dev;
+ struct yaffs_SearchContext *sc;
struct inode *inode = f->f_dentry->d_inode;
unsigned long offset, curoffs;
- struct ylist_head *i;
yaffs_Object *l;
+ int retVal = 0;
char name[YAFFS_MAX_NAME_LENGTH + 1];
offset = f->f_pos;
+ sc = yaffs_NewSearch(obj);
+ if(!sc){
+ retVal = -ENOMEM;
+ goto unlock_out;
+ }
+
T(YAFFS_TRACE_OS, ("yaffs_readdir: starting at %d\n", (int)offset));
if (offset == 0) {
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++;
}
f->f_version = inode->i_version;
}
- ylist_for_each(i, &obj->variant.directoryVariant.children) {
+ while(sc->nextReturn){
curoffs++;
+ l = sc->nextReturn;
if (curoffs >= offset) {
- l = ylist_entry(i, yaffs_Object, siblings);
+ int this_inode = yaffs_GetObjectInode(l);
+ int this_type = yaffs_GetObjectType(l);
yaffs_GetObjectName(l, name,
YAFFS_MAX_NAME_LENGTH + 1);
("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;
+ this_inode,
+ this_type) < 0)
+ goto out;
+
+ yaffs_GrossLock(dev);
offset++;
f->f_pos++;
}
+ yaffs_SearchAdvance(sc);
}
-up_and_out:
-out:
+unlock_out:
yaffs_GrossUnlock(dev);
+out:
+ yaffs_EndSearch(sc);
- return 0;
+ return retVal;
}
/*
dev = obj->myDev;
- T(YAFFS_TRACE_OS, ("yaffs_sync_object\n"));
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_sync_object\n"));
yaffs_GrossLock(dev);
yaffs_FlushFile(obj, 1, datasync);
yaffs_GrossUnlock(dev);
static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
- int error;
+ int error = 0;
yaffs_Device *dev;
T(YAFFS_TRACE_OS,
("yaffs_setattr of object %d\n",
yaffs_InodeToObject(inode)->objectId));
- error = inode_change_ok(inode, attr);
+ /* Fail if a requested resize >= 2GB */
+ if (attr->ia_valid & ATTR_SIZE &&
+ (attr->ia_size >> 31))
+ error = -EINVAL;
+
+ if (error == 0)
+ error = inode_change_ok(inode, attr);
if (error == 0) {
+ int result;
+ if (!error){
+ error = inode_setattr(inode, attr);
+ T(YAFFS_TRACE_OS,("inode_setattr called\n"));
+ if (attr->ia_valid & ATTR_SIZE)
+ truncate_inode_pages(&inode->i_data,attr->ia_size);
+ }
dev = yaffs_InodeToObject(inode)->myDev;
+ if (attr->ia_valid & ATTR_SIZE){
+ T(YAFFS_TRACE_OS,("resize to %d(%x)\n",(int)(attr->ia_size),(int)(attr->ia_size)));
+ }
yaffs_GrossLock(dev);
- if (yaffs_SetAttributes(yaffs_InodeToObject(inode), attr) ==
- YAFFS_OK) {
+ result = yaffs_SetAttributes(yaffs_InodeToObject(inode), attr);
+ if(result == YAFFS_OK) {
error = 0;
} else {
error = -EPERM;
}
yaffs_GrossUnlock(dev);
- if (!error)
- error = inode_setattr(inode, attr);
+
}
+
+ T(YAFFS_TRACE_OS,
+ ("yaffs_setattr done returning %d\n",error));
+
return error;
}
uint64_t bytesInDev;
uint64_t bytesFree;
- bytesInDev = ((uint64_t)((dev->endBlock - dev->startBlock + 1))) *
- ((uint64_t)(dev->nChunksPerBlock * dev->nDataBytesPerChunk));
+ bytesInDev = ((uint64_t)((dev->param.endBlock - dev->param.startBlock + 1))) *
+ ((uint64_t)(dev->param.nChunksPerBlock * dev->nDataBytesPerChunk));
do_div(bytesInDev, sb->s_blocksize); /* bytesInDev becomes the number of blocks */
buf->f_blocks = bytesInDev;
} else if (sb->s_blocksize > dev->nDataBytesPerChunk) {
buf->f_blocks =
- (dev->endBlock - dev->startBlock + 1) *
- dev->nChunksPerBlock /
+ (dev->param.endBlock - dev->param.startBlock + 1) *
+ dev->param.nChunksPerBlock /
(sb->s_blocksize / dev->nDataBytesPerChunk);
buf->f_bfree =
yaffs_GetNumberOfFreeChunks(dev) /
(sb->s_blocksize / dev->nDataBytesPerChunk);
} else {
buf->f_blocks =
- (dev->endBlock - dev->startBlock + 1) *
- dev->nChunksPerBlock *
+ (dev->param.endBlock - dev->param.startBlock + 1) *
+ dev->param.nChunksPerBlock *
(dev->nDataBytesPerChunk / sb->s_blocksize);
buf->f_bfree =
-static void yaffs_flush_sb_inodes(struct super_block *sb)
+static void yaffs_FlushInodes(struct super_block *sb)
{
struct inode *iptr;
yaffs_Object *obj;
}
}
-static int yaffs_do_sync_fs(struct super_block *sb)
+
+static void yaffs_FlushSuperBlock(struct super_block *sb, int do_checkpoint)
+{
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+ if(!dev)
+ return;
+
+ yaffs_FlushInodes(sb);
+ yaffs_UpdateDirtyDirectories(dev);
+ yaffs_FlushEntireDeviceCache(dev);
+ if(do_checkpoint)
+ yaffs_CheckpointSave(dev);
+}
+
+static int yaffs_do_sync_fs(struct super_block *sb, int do_checkpoint)
{
yaffs_Device *dev = yaffs_SuperToDevice(sb);
- T(YAFFS_TRACE_OS, ("yaffs_do_sync_fs\n"));
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC,
+ ("yaffs_do_sync_fs: %s %s\n",
+ sb->s_dirt ? "dirty" : "clean",
+ do_checkpoint ? "with checkpoint" : "no checkpoint"));
if (sb->s_dirt) {
yaffs_GrossLock(dev);
+ yaffs_FlushSuperBlock(sb,do_checkpoint);
+ yaffs_GrossUnlock(dev);
- if (dev) {
- yaffs_FlushEntireDeviceCache(dev);
- yaffs_flush_sb_inodes(sb);
- yaffs_CheckpointSave(dev);
- }
+ sb->s_dirt = 0;
+ }
+ return 0;
+}
+
+/*
+ * yaffs background thread functions .
+ * yaffs_BackgroundThread() the thread function
+ * yaffs_BackgroundStart() launches the background thread.
+ * yaffs_BackgroundStop() cleans up the background thread.
+ *
+ * NB:
+ * The thread should only run after the yaffs is initialised
+ * The thread should be stopped before yaffs is unmounted.
+ * The thread should not do any writing while the fs is in read only.
+ */
+
+#ifdef YAFFS_COMPILE_BACKGROUND
+static int yaffs_BackgroundThread(void *data)
+{
+ yaffs_Device *dev = (yaffs_Device *)data;
+ struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev);
+ T(YAFFS_TRACE_BACKGROUND,
+ ("yaffs_background starting for dev %p\n",
+ (void *)dev));
+
+ while(context->bgRunning){
+ T(YAFFS_TRACE_BACKGROUND,
+ ("yaffs_background\n"));
+
+ if(kthread_should_stop())
+ break;
+ yaffs_GrossLock(dev);
+ yaffs_UpdateDirtyDirectories(dev);
yaffs_GrossUnlock(dev);
+ msleep(500);
+ }
+ return 0;
+}
- sb->s_dirt = 0;
+static int yaffs_BackgroundStart(yaffs_Device *dev)
+{
+ int retval = 0;
+
+ struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev);
+
+ context->bgRunning = 1;
+
+ context->bgThread = kthread_run(yaffs_BackgroundThread,(void *)dev,"yaffs_%x",(unsigned)dev);
+
+ if(IS_ERR(context->bgThread)){
+ retval = PTR_ERR(context->bgThread);
+ context->bgThread = NULL;
+ context->bgRunning = 0;
}
+ return retval;
+}
+
+static void yaffs_BackgroundStop(yaffs_Device *dev)
+{
+ struct yaffs_LinuxContext *ctxt = yaffs_DeviceToContext(dev);
+
+ ctxt->bgRunning = 0;
+
+ if( ctxt->bgThread){
+ kthread_stop(ctxt->bgThread);
+ ctxt->bgThread = NULL;
+ }
+}
+#else
+static int yaffs_BackgroundThread(void *data)
+{
+ return 0;
+}
+
+static int yaffs_BackgroundStart(yaffs_Device *dev)
+{
return 0;
}
+static void yaffs_BackgroundStop(yaffs_Device *dev)
+{
+}
+#endif
+
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
static void yaffs_write_super(struct super_block *sb)
#endif
{
- T(YAFFS_TRACE_OS, ("yaffs_write_super\n"));
- if (yaffs_auto_checkpoint >= 2)
- yaffs_do_sync_fs(sb);
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_write_super\n"));
+ yaffs_do_sync_fs(sb, yaffs_auto_checkpoint >= 2);
+
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
return 0;
#endif
static int yaffs_sync_fs(struct super_block *sb)
#endif
{
- T(YAFFS_TRACE_OS, ("yaffs_sync_fs\n"));
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_sync_fs\n"));
- if (yaffs_auto_checkpoint >= 1)
- yaffs_do_sync_fs(sb);
+ yaffs_do_sync_fs(sb,yaffs_auto_checkpoint >= 1);
return 0;
}
#endif
-static YLIST_HEAD(yaffs_dev_list);
+static YLIST_HEAD(yaffs_context_list);
+struct semaphore yaffs_context_lock;
#if 0 /* not used */
static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
yaffs_GrossLock(dev);
- yaffs_FlushEntireDeviceCache(dev);
-
- yaffs_CheckpointSave(dev);
+ yaffs_FlushSuperBlock(sb,1);
if (mtd->sync)
mtd->sync(mtd);
yaffs_GrossLock(dev);
- yaffs_FlushEntireDeviceCache(dev);
+ yaffs_FlushSuperBlock(sb,1);
- yaffs_CheckpointSave(dev);
+ if (yaffs_DeviceToContext(dev)->putSuperFunc)
+ yaffs_DeviceToContext(dev)->putSuperFunc(sb);
- if (dev->putSuperFunc)
- dev->putSuperFunc(sb);
+ yaffs_BackgroundStop(dev);
yaffs_Deinitialise(dev);
yaffs_GrossUnlock(dev);
- /* we assume this is protected by lock_kernel() in mount/umount */
- ylist_del(&dev->devList);
+ down(&yaffs_context_lock);
+ ylist_del_init(&(yaffs_DeviceToContext(dev)->contextList));
+ up(&yaffs_context_lock);
- if (dev->spareBuffer) {
- YFREE(dev->spareBuffer);
- dev->spareBuffer = NULL;
+ if (yaffs_DeviceToContext(dev)->spareBuffer) {
+ YFREE(yaffs_DeviceToContext(dev)->spareBuffer);
+ yaffs_DeviceToContext(dev)->spareBuffer = NULL;
}
kfree(dev);
static void yaffs_MTDPutSuper(struct super_block *sb)
{
- struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice;
+ struct mtd_info *mtd = yaffs_DeviceToContext(yaffs_SuperToDevice(sb))->mtd;
if (mtd->sync)
mtd->sync(mtd);
}
-static void yaffs_MarkSuperBlockDirty(void *vsb)
+static void yaffs_MarkSuperBlockDirty(yaffs_Device *dev)
{
- struct super_block *sb = (struct super_block *)vsb;
+ struct super_block *sb = yaffs_DeviceToContext(dev)->superBlock;
T(YAFFS_TRACE_OS, ("yaffs_MarkSuperBlockDirty() sb = %p\n", sb));
if (sb)
int skip_checkpoint_read;
int skip_checkpoint_write;
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];
memset(cur_opt, 0, MAX_OPT_LEN + 1);
p = 0;
+ while(*options_str == ',')
+ options_str++;
+
while (*options_str && *options_str != ',') {
if (p < MAX_OPT_LEN) {
cur_opt[p] = *options_str;
if (!strcmp(cur_opt, "inband-tags"))
options->inband_tags = 1;
- else if (!strcmp(cur_opt, "no-cache"))
+ else if (!strcmp(cur_opt, "tags-ecc-off")){
+ options->tags_ecc_on = 0;
+ options->tags_ecc_overridden=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-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"))
options->skip_checkpoint_read = 1;
struct mtd_info *mtd;
int err;
char *data_str = (char *)data;
+ struct yaffs_LinuxContext *context = NULL;
+ yaffs_DeviceParam *param;
yaffs_options options;
* Set the yaffs_Device up for mtd
*/
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
- sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL);
-#else
- sb->u.generic_sbp = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL);
-#endif
+ dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL);
+ context = kmalloc(sizeof(struct yaffs_LinuxContext),GFP_KERNEL);
+
+ if(!dev || !context ){
+ if(dev)
+ kfree(dev);
+ if(context)
+ kfree(context);
+ dev = NULL;
+ context = NULL;
+ }
+
if (!dev) {
/* Deep shit could not allocate device structure */
T(YAFFS_TRACE_ALWAYS,
"yaffs_Device. \n"));
return NULL;
}
-
memset(dev, 0, sizeof(yaffs_Device));
- dev->genericDevice = mtd;
- dev->name = mtd->name;
+ param = &(dev->param);
+
+ memset(context,0,sizeof(struct yaffs_LinuxContext));
+ dev->context = context;
+ YINIT_LIST_HEAD(&(context->contextList));
+ context->dev = dev;
+ context->superBlock = sb;
+
+
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
+ sb->s_fs_info = dev;
+#else
+ sb->u.generic_sbp = dev;
+#endif
+
+ yaffs_DeviceToContext(dev)->mtd = mtd;
+ param->name = mtd->name;
/* Set up the memory size parameters.... */
nBlocks = YCALCBLOCKS(mtd->size, (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK));
- dev->startBlock = 0;
- dev->endBlock = nBlocks - 1;
- dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
- dev->totalBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
- dev->nReservedBlocks = 5;
- dev->nShortOpCaches = (options.no_cache) ? 0 : 10;
- dev->inbandTags = options.inband_tags;
+ param->startBlock = 0;
+ param->endBlock = nBlocks - 1;
+ param->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+ param->totalBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+ param->nReservedBlocks = 5;
+ param->nShortOpCaches = (options.no_cache) ? 0 : 10;
+ param->inbandTags = options.inband_tags;
+
+#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD
+ param->disableLazyLoad = 1;
+#endif
+ if(options.lazy_loading_overridden)
+ param->disableLazyLoad = !options.lazy_loading_enabled;
+
+#ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC
+ param->noTagsECC = 1;
+#endif
+
+#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND
+#else
+ param->deferDirectoryUpdate = 1;
+#endif
+
+ if(options.tags_ecc_overridden)
+ param->noTagsECC = !options.tags_ecc_on;
+
+#ifdef CONFIG_YAFFS_EMPTY_LOST_AND_FOUND
+ param->emptyLostAndFound = 1;
+#endif
+
+#ifdef CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING
+ param->refreshPeriod = 0;
+#else
+ param->refreshPeriod = 10000;
+#endif
+
+ if(options.empty_lost_and_found_overridden)
+ param->emptyLostAndFound = options.empty_lost_and_found;
/* ... and the functions. */
if (yaffsVersion == 2) {
- dev->writeChunkWithTagsToNAND =
+ param->writeChunkWithTagsToNAND =
nandmtd2_WriteChunkWithTagsToNAND;
- dev->readChunkWithTagsFromNAND =
+ param->readChunkWithTagsFromNAND =
nandmtd2_ReadChunkWithTagsFromNAND;
- dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
- dev->queryNANDBlock = nandmtd2_QueryNANDBlock;
- dev->spareBuffer = YMALLOC(mtd->oobsize);
- dev->isYaffs2 = 1;
+ param->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
+ param->queryNANDBlock = nandmtd2_QueryNANDBlock;
+ yaffs_DeviceToContext(dev)->spareBuffer = YMALLOC(mtd->oobsize);
+ param->isYaffs2 = 1;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
- dev->totalBytesPerChunk = mtd->writesize;
- dev->nChunksPerBlock = mtd->erasesize / mtd->writesize;
+ param->totalBytesPerChunk = mtd->writesize;
+ param->nChunksPerBlock = mtd->erasesize / mtd->writesize;
#else
- dev->totalBytesPerChunk = mtd->oobblock;
- dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
+ param->totalBytesPerChunk = mtd->oobblock;
+ param->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
#endif
nBlocks = YCALCBLOCKS(mtd->size, mtd->erasesize);
- dev->startBlock = 0;
- dev->endBlock = nBlocks - 1;
+ param->startBlock = 0;
+ param->endBlock = nBlocks - 1;
} else {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
/* use the MTD interface in yaffs_mtdif1.c */
- dev->writeChunkWithTagsToNAND =
+ param->writeChunkWithTagsToNAND =
nandmtd1_WriteChunkWithTagsToNAND;
- dev->readChunkWithTagsFromNAND =
+ param->readChunkWithTagsFromNAND =
nandmtd1_ReadChunkWithTagsFromNAND;
- dev->markNANDBlockBad = nandmtd1_MarkNANDBlockBad;
- dev->queryNANDBlock = nandmtd1_QueryNANDBlock;
+ param->markNANDBlockBad = nandmtd1_MarkNANDBlockBad;
+ param->queryNANDBlock = nandmtd1_QueryNANDBlock;
#else
- dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
- dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
+ param->writeChunkToNAND = nandmtd_WriteChunkToNAND;
+ param->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
#endif
- dev->isYaffs2 = 0;
+ param->isYaffs2 = 0;
}
/* ... and common functions */
- dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
- dev->initialiseNAND = nandmtd_InitialiseNAND;
+ param->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
+ param->initialiseNAND = nandmtd_InitialiseNAND;
- dev->putSuperFunc = yaffs_MTDPutSuper;
+ yaffs_DeviceToContext(dev)->putSuperFunc = yaffs_MTDPutSuper;
- dev->superBlock = (void *)sb;
- dev->markSuperBlockDirty = yaffs_MarkSuperBlockDirty;
+ param->markSuperBlockDirty = yaffs_MarkSuperBlockDirty;
+ yaffs_DeviceToContext(dev)->superBlock= sb;
+
#ifndef CONFIG_YAFFS_DOES_ECC
- dev->useNANDECC = 1;
+ param->useNANDECC = 1;
#endif
#ifdef CONFIG_YAFFS_DISABLE_WIDE_TNODES
- dev->wideTnodesDisabled = 1;
+ param->wideTnodesDisabled = 1;
#endif
- dev->skipCheckpointRead = options.skip_checkpoint_read;
- dev->skipCheckpointWrite = options.skip_checkpoint_write;
+ param->skipCheckpointRead = options.skip_checkpoint_read;
+ param->skipCheckpointWrite = options.skip_checkpoint_write;
/* we assume this is protected by lock_kernel() in mount/umount */
- ylist_add_tail(&dev->devList, &yaffs_dev_list);
+ down(&yaffs_context_lock);
+ ylist_add_tail(&(yaffs_DeviceToContext(dev)->contextList), &yaffs_context_list);
+ up(&yaffs_context_lock);
- init_MUTEX(&dev->grossLock);
+ /* Directory search handling...*/
+ YINIT_LIST_HEAD(&(yaffs_DeviceToContext(dev)->searchContexts));
+ param->removeObjectCallback = yaffs_RemoveObjectCallback;
+
+ init_MUTEX(&(yaffs_DeviceToContext(dev)->grossLock));
yaffs_GrossLock(dev);
T(YAFFS_TRACE_OS,
("yaffs_read_super: guts initialised %s\n",
(err == YAFFS_OK) ? "OK" : "FAILED"));
+
+ if(err == YAFFS_OK)
+ yaffs_BackgroundStart(dev);
+
+ if(!context->bgThread)
+ param->deferDirectoryUpdate = 0;
+
/* Release lock before yaffs_get_inode() */
yaffs_GrossUnlock(dev);
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->param.startBlock);
+ buf += sprintf(buf, "endBlock........... %d\n", dev->param.endBlock);
+ buf += sprintf(buf, "totalBytesPerChunk. %d\n", dev->param.totalBytesPerChunk);
+ buf += sprintf(buf, "useNANDECC......... %d\n", dev->param.useNANDECC);
+ buf += sprintf(buf, "noTagsECC.......... %d\n", dev->param.noTagsECC);
+ buf += sprintf(buf, "isYaffs2........... %d\n", dev->param.isYaffs2);
+ buf += sprintf(buf, "inbandTags......... %d\n", dev->param.inbandTags);
+ buf += sprintf(buf, "emptyLostAndFound.. %d\n", dev->param.emptyLostAndFound);
+ buf += sprintf(buf, "disableLazyLoad.... %d\n", dev->param.disableLazyLoad);
+ buf += sprintf(buf, "refreshPeriod...... %d\n", dev->param.refreshPeriod);
+ buf += sprintf(buf, "nShortOpCaches..... %d\n", dev->param.nShortOpCaches);
+ buf += sprintf(buf, "nReservedBlocks.... %d\n", dev->param.nReservedBlocks);
+
+ buf += sprintf(buf, "\n");
+
+ return buf;
+}
+
+
+static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev)
{
- buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock);
- buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock);
- buf += sprintf(buf, "totalBytesPerChunk. %d\n", dev->totalBytesPerChunk);
buf += sprintf(buf, "nDataBytesPerChunk. %d\n", dev->nDataBytesPerChunk);
buf += sprintf(buf, "chunkGroupBits..... %d\n", dev->chunkGroupBits);
buf += sprintf(buf, "chunkGroupSize..... %d\n", dev->chunkGroupSize);
buf += sprintf(buf, "nErasedBlocks...... %d\n", dev->nErasedBlocks);
- buf += sprintf(buf, "nReservedBlocks.... %d\n", dev->nReservedBlocks);
buf += sprintf(buf, "blocksInCheckpoint. %d\n", dev->blocksInCheckpoint);
+ buf += sprintf(buf, "\n");
buf += sprintf(buf, "nTnodesCreated..... %d\n", dev->nTnodesCreated);
buf += sprintf(buf, "nFreeTnodes........ %d\n", dev->nFreeTnodes);
buf += sprintf(buf, "nObjectsCreated.... %d\n", dev->nObjectsCreated);
buf += sprintf(buf, "nFreeObjects....... %d\n", dev->nFreeObjects);
buf += sprintf(buf, "nFreeChunks........ %d\n", dev->nFreeChunks);
- buf += sprintf(buf, "nPageWrites........ %d\n", dev->nPageWrites);
- buf += sprintf(buf, "nPageReads......... %d\n", dev->nPageReads);
- buf += sprintf(buf, "nBlockErasures..... %d\n", dev->nBlockErasures);
- buf += sprintf(buf, "nGCCopies.......... %d\n", dev->nGCCopies);
- buf += sprintf(buf, "garbageCollections. %d\n", dev->garbageCollections);
- buf += sprintf(buf, "passiveGCs......... %d\n",
- dev->passiveGarbageCollections);
- buf += sprintf(buf, "nRetriedWrites..... %d\n", dev->nRetriedWrites);
- buf += sprintf(buf, "nShortOpCaches..... %d\n", dev->nShortOpCaches);
- buf += sprintf(buf, "nRetireBlocks...... %d\n", dev->nRetiredBlocks);
- buf += sprintf(buf, "eccFixed........... %d\n", dev->eccFixed);
- buf += sprintf(buf, "eccUnfixed......... %d\n", dev->eccUnfixed);
- buf += sprintf(buf, "tagsEccFixed....... %d\n", dev->tagsEccFixed);
- buf += sprintf(buf, "tagsEccUnfixed..... %d\n", dev->tagsEccUnfixed);
- buf += sprintf(buf, "cacheHits.......... %d\n", dev->cacheHits);
- buf += sprintf(buf, "nDeletedFiles...... %d\n", dev->nDeletedFiles);
- buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles);
+ buf += sprintf(buf, "\n");
+ buf += sprintf(buf, "nPageWrites........ %u\n", dev->nPageWrites);
+ buf += sprintf(buf, "nPageReads......... %u\n", dev->nPageReads);
+ buf += sprintf(buf, "nBlockErasures..... %u\n", dev->nBlockErasures);
+ buf += sprintf(buf, "nGCCopies.......... %u\n", dev->nGCCopies);
+ buf += sprintf(buf, "garbageCollections. %u\n", dev->garbageCollections);
+ buf += sprintf(buf, "passiveGCs......... %u\n", dev->passiveGarbageCollections);
+ buf += sprintf(buf, "nRetriedWrites..... %u\n", dev->nRetriedWrites);
+ buf += sprintf(buf, "nRetireBlocks...... %u\n", dev->nRetiredBlocks);
+ buf += sprintf(buf, "eccFixed........... %u\n", dev->eccFixed);
+ buf += sprintf(buf, "eccUnfixed......... %u\n", dev->eccUnfixed);
+ buf += sprintf(buf, "tagsEccFixed....... %u\n", dev->tagsEccFixed);
+ buf += sprintf(buf, "tagsEccUnfixed..... %u\n", dev->tagsEccUnfixed);
+ buf += sprintf(buf, "cacheHits.......... %u\n", dev->cacheHits);
+ buf += sprintf(buf, "nDeletedFiles...... %u\n", dev->nDeletedFiles);
+ buf += sprintf(buf, "nUnlinkedFiles..... %u\n", dev->nUnlinkedFiles);
+ buf += sprintf(buf, "refreshCount....... %u\n", dev->refreshCount);
buf +=
- sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions);
- buf += sprintf(buf, "useNANDECC......... %d\n", dev->useNANDECC);
- buf += sprintf(buf, "isYaffs2........... %d\n", dev->isYaffs2);
- buf += sprintf(buf, "inbandTags......... %d\n", dev->inbandTags);
+ sprintf(buf, "nBackgroudDeletions %u\n", dev->nBackgroundDeletions);
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);
- }
+ else if (step == 1)
+ buf += sprintf(buf,"\n");
+ else {
+ step-=2;
+
+ down(&yaffs_context_lock);
- /* 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_context_list) {
+ struct yaffs_LinuxContext *dc = ylist_entry(item, struct yaffs_LinuxContext, contextList);
+ yaffs_Device *dev = dc->dev;
- /* 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;
+ if (n < (step & ~1)) {
+ n+=2;
+ continue;
+ }
+ if((step & 1)==0){
+ buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->param.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;
+ up(&yaffs_context_lock);
}
- unlock_kernel();
return buf - page < count ? buf - page : count;
}
{"scan_debug", YAFFS_TRACE_SCAN_DEBUG},
{"scan", YAFFS_TRACE_SCAN},
{"tracing", YAFFS_TRACE_TRACING},
+ {"sync", YAFFS_TRACE_SYNC},
+
+ {"background", YAFFS_TRACE_BACKGROUND},
{"verify", YAFFS_TRACE_VERIFY},
{"verify_nand", YAFFS_TRACE_VERIFY_NAND},
};
#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;
T(YAFFS_TRACE_ALWAYS,
("yaffs " __DATE__ " " __TIME__ " Installing. \n"));
+ init_MUTEX(&yaffs_context_lock);
+
/* Install the proc_fs entry */
my_proc_entry = create_proc_entry("yaffs",
S_IRUGO | S_IFREG,