#define YAFFS_COMPILE_EXPORTFS
#endif
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35))
+#define YAFFS_USE_SETATTR_COPY
+#define YAFFS_USE_TRUNCATE_SETSIZE
+#endif
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35))
+#define YAFFS_HAS_EVICT_INODE
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13))
+#define YAFFS_NEW_FOLLOW_LINK 1
+#else
+#define YAFFS_NEW_FOLLOW_LINK 0
+#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))
#include <linux/config.h>
#include <linux/string.h>
#include <linux/ctype.h>
+#if (YAFFS_NEW_FOLLOW_LINK == 1)
+#include <linux/namei.h>
+#endif
+
#ifdef YAFFS_COMPILE_EXPORTFS
#include <linux/exportfs.h>
#endif
unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS;
unsigned int yaffs_auto_checkpoint = 1;
unsigned int yaffs_gc_control = 1;
+unsigned int yaffs_bg_enable = 1;
/* Module Parameters */
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
module_param(yaffs_wr_attempts, uint, 0644);
module_param(yaffs_auto_checkpoint, uint, 0644);
module_param(yaffs_gc_control, uint, 0644);
+module_param(yaffs_bg_enable, uint, 0644);
#else
MODULE_PARM(yaffs_traceMask, "i");
MODULE_PARM(yaffs_wr_attempts, "i");
static void yaffs_put_inode(struct inode *inode);
#endif
+#ifdef YAFFS_HAS_EVICT_INODE
+static void yaffs_evict_inode(struct inode *);
+#else
static void yaffs_delete_inode(struct inode *);
static void yaffs_clear_inode(struct inode *);
+#endif
static int yaffs_readpage(struct file *file, struct page *page);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
static int yaffs_readlink(struct dentry *dentry, char __user *buffer,
int buflen);
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13))
+#if (YAFFS_NEW_FOLLOW_LINK == 1)
+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias);
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 void yaffs_MarkSuperBlockDirty(yaffs_Device *dev);
+
static loff_t yaffs_dir_llseek(struct file *file, loff_t offset, int origin);
+static int yaffs_vfs_setattr(struct inode *, struct iattr *);
+
+
static struct address_space_operations yaffs_file_address_operations = {
.readpage = yaffs_readpage,
.writepage = yaffs_writepage,
#endif
};
+
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
static const struct file_operations yaffs_file_operations = {
.read = do_sync_read,
static const struct inode_operations yaffs_symlink_inode_operations = {
.readlink = yaffs_readlink,
.follow_link = yaffs_follow_link,
+#if (YAFFS_NEW_FOLLOW_LINK == 1)
+ .put_link = yaffs_put_link,
+#endif
.setattr = yaffs_setattr,
#ifdef CONFIG_YAFFS_XATTR
.setxattr = yaffs_setxattr,
.put_inode = yaffs_put_inode,
#endif
.put_super = yaffs_put_super,
+#ifdef YAFFS_HAS_EVICT_INODE
+ .evict_inode = yaffs_evict_inode,
+#else
.delete_inode = yaffs_delete_inode,
.clear_inode = yaffs_clear_inode,
+#endif
.sync_fs = yaffs_sync_fs,
.write_super = yaffs_write_super,
};
+
+static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr)
+{
+#ifdef YAFFS_USE_SETATTR_COPY
+ setattr_copy(inode,attr);
+ return 0;
+#else
+ return inode_setattr(inode, attr);
+#endif
+
+}
+
+static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize)
+{
+#ifdef YAFFS_USE_TRUNCATE_SETSIZE
+ truncate_setsize(inode,newsize);
+ return 0;
+#else
+ return simple_setsize(inode, newsize);
+#endif
+
+}
+
static unsigned yaffs_gc_control_callback(yaffs_Device *dev)
{
return yaffs_gc_control;
static void yaffs_GrossLock(yaffs_Device *dev)
{
T(YAFFS_TRACE_LOCK, (TSTR("yaffs locking %p\n"), current));
- down(&(yaffs_DeviceToContext(dev)->grossLock));
+ down(&(yaffs_DeviceToLC(dev)->grossLock));
T(YAFFS_TRACE_LOCK, (TSTR("yaffs locked %p\n"), current));
}
static void yaffs_GrossUnlock(yaffs_Device *dev)
{
T(YAFFS_TRACE_LOCK, (TSTR("yaffs unlocking %p\n"), current));
- up(&(yaffs_DeviceToContext(dev)->grossLock));
+ up(&(yaffs_DeviceToLC(dev)->grossLock));
}
#ifdef YAFFS_COMPILE_EXPORTFS
dir->variant.directoryVariant.children.next,
yaffs_Object,siblings);
YINIT_LIST_HEAD(&sc->others);
- ylist_add(&sc->others,&(yaffs_DeviceToContext(dev)->searchContexts));
+ ylist_add(&sc->others,&(yaffs_DeviceToLC(dev)->searchContexts));
}
return sc;
}
struct ylist_head *i;
struct yaffs_SearchContext *sc;
- struct ylist_head *search_contexts = &(yaffs_DeviceToContext(obj->myDev)->searchContexts);
+ struct ylist_head *search_contexts = &(yaffs_DeviceToLC(obj->myDev)->searchContexts);
/* Iterate through the directory search contexts.
return ret;
}
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13))
+#if (YAFFS_NEW_FOLLOW_LINK == 1)
static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
#else
static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
yaffs_GrossLock(dev);
alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
-
yaffs_GrossUnlock(dev);
if (!alias) {
goto out;
}
- ret = vfs_follow_link(nd, alias);
- kfree(alias);
+#if (YAFFS_NEW_FOLLOW_LINK == 1)
+ nd_set_link(nd, alias);
+ ret = (int)alias;
out:
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13))
return ERR_PTR(ret);
#else
+ ret = vfs_follow_link(nd, alias);
+ kfree(alias);
+out:
return ret;
#endif
}
+#if (YAFFS_NEW_FOLLOW_LINK == 1)
+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) {
+ kfree(alias);
+}
+#endif
+
struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,
yaffs_Object *obj);
yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
- if(current != yaffs_DeviceToContext(dev)->readdirProcess)
+ if(current != yaffs_DeviceToLC(dev)->readdirProcess)
yaffs_GrossLock(dev);
T(YAFFS_TRACE_OS,
obj = yaffs_GetEquivalentObject(obj); /* in case it was a hardlink */
/* Can't hold gross lock when calling yaffs_get_inode() */
- if(current != yaffs_DeviceToContext(dev)->readdirProcess)
+ if(current != yaffs_DeviceToLC(dev)->readdirProcess)
yaffs_GrossUnlock(dev);
if (obj) {
}
#endif
-/* clear is called to tell the fs to release any per-inode data it holds */
-static void yaffs_clear_inode(struct inode *inode)
+
+static void yaffs_UnstitchObject(struct inode *inode, yaffs_Object *obj)
+{
+ /* Clear the association between the inode and
+ * the yaffs_Object.
+ */
+ obj->myInode = NULL;
+ yaffs_InodeToObjectLV(inode) = NULL;
+
+ /* If the object freeing was deferred, then the real
+ * free happens now.
+ * This should fix the inode inconsistency problem.
+ */
+ yaffs_HandleDeferedFree(obj);
+}
+
+#ifdef YAFFS_HAS_EVICT_INODE
+/* yaffs_evict_inode combines into one operation what was previously done in
+ * yaffs_clear_inode() and yaffs_delete_inode()
+ *
+ */
+static void yaffs_evict_inode( struct inode *inode)
{
yaffs_Object *obj;
yaffs_Device *dev;
+ int deleteme = 0;
obj = yaffs_InodeToObject(inode);
T(YAFFS_TRACE_OS,
- (TSTR("yaffs_clear_inode: ino %d, count %d %s\n"), (int)inode->i_ino,
+ (TSTR("yaffs_evict_inode: ino %d, count %d %s\n"), (int)inode->i_ino,
atomic_read(&inode->i_count),
obj ? "object exists" : "null object"));
+ if (!inode->i_nlink && !is_bad_inode(inode))
+ deleteme = 1;
+ truncate_inode_pages(&inode->i_data,0);
+ end_writeback(inode);
+
+ if(deleteme && obj){
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
+ yaffs_DeleteObject(obj);
+ yaffs_GrossUnlock(dev);
+ }
if (obj) {
dev = obj->myDev;
yaffs_GrossLock(dev);
+ yaffs_UnstitchObject(inode,obj);
+ yaffs_GrossUnlock(dev);
+ }
+
- /* Clear the association between the inode and
- * the yaffs_Object.
- */
- obj->myInode = NULL;
- yaffs_InodeToObjectLV(inode) = NULL;
+}
+#else
+
+/* clear is called to tell the fs to release any per-inode data it holds.
+ * The object might still exist on disk and is just being thrown out of the cache
+ * or else the object has actually been deleted and we're being called via
+ * the chain
+ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode()
+ */
+
+static void yaffs_clear_inode(struct inode *inode)
+{
+ yaffs_Object *obj;
+ yaffs_Device *dev;
- /* If the object freeing was deferred, then the real
- * free happens now.
- * This should fix the inode inconsistency problem.
- */
+ obj = yaffs_InodeToObject(inode);
- yaffs_HandleDeferedFree(obj);
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_clear_inode: ino %d, count %d %s\n"), (int)inode->i_ino,
+ atomic_read(&inode->i_count),
+ obj ? "object exists" : "null object"));
+ if (obj) {
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
+ yaffs_UnstitchObject(inode,obj);
yaffs_GrossUnlock(dev);
}
#endif
clear_inode(inode);
}
+#endif
+
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
static int yaffs_file_flush(struct file *file, fl_owner_t id)
static int yaffs_writepage(struct page *page)
#endif
{
+ yaffs_Device *dev;
struct address_space *mapping = page->mapping;
struct inode *inode;
unsigned long end_index;
buffer = kmap(page);
obj = yaffs_InodeToObject(inode);
- yaffs_GrossLock(obj->myDev);
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
T(YAFFS_TRACE_OS,
(TSTR("yaffs_writepage at %08x, size %08x\n"),
nWritten = yaffs_WriteDataToFile(obj, buffer,
page->index << PAGE_CACHE_SHIFT, nBytes, 0);
+ yaffs_MarkSuperBlockDirty(dev);
+
T(YAFFS_TRACE_OS,
(TSTR("writepag1: obj = %05x, ino = %05x\n"),
(int)obj->variant.fileVariant.fileSize, (int)inode->i_size));
- yaffs_GrossUnlock(obj->myDev);
+ yaffs_GrossUnlock(dev);
kunmap(page);
set_page_writeback(page);
nWritten = yaffs_WriteDataToFile(obj, buf, ipos, n, 0);
+ yaffs_MarkSuperBlockDirty(dev);
+
T(YAFFS_TRACE_OS,
(TSTR("yaffs_file_write: %d(%x) bytes written\n"),
(unsigned )n,(unsigned)n));
yaffs_GrossLock(dev);
- yaffs_DeviceToContext(dev)->readdirProcess = current;
+ yaffs_DeviceToLC(dev)->readdirProcess = current;
offset = f->f_pos;
out:
yaffs_EndSearch(sc);
- yaffs_DeviceToContext(dev)->readdirProcess = NULL;
+ yaffs_DeviceToLC(dev)->readdirProcess = NULL;
yaffs_GrossUnlock(dev);
return retVal;
if (error == 0) {
int result;
if (!error){
- error = inode_setattr(inode, attr);
+ error = yaffs_vfs_setattr(inode, attr);
T(YAFFS_TRACE_OS,(TSTR("inode_setattr called\n")));
- if (attr->ia_valid & ATTR_SIZE)
- truncate_inode_pages(&inode->i_data,attr->ia_size);
+ if (attr->ia_valid & ATTR_SIZE){
+ yaffs_vfs_setsize(inode,attr->ia_size);
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+ }
}
dev = yaffs_InodeToObject(inode)->myDev;
if (attr->ia_valid & ATTR_SIZE){
yaffs_Object *obj = yaffs_InodeToObject(inode);
T(YAFFS_TRACE_OS,
- (TSTR("yaffs_getxattr of object %d\n"),
- obj->objectId));
-
+ (TSTR("yaffs_getxattr \"%s\" from object %d\n"),
+ name, obj->objectId));
if (error == 0) {
dev = obj->myDev;
static unsigned yaffs_bg_gc_urgency(yaffs_Device *dev)
{
unsigned erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
- struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev);
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
unsigned scatteredFree = 0; /* Free chunks not in an erased block */
if(erasedChunks < dev->nFreeChunks)
static int yaffs_BackgroundThread(void *data)
{
yaffs_Device *dev = (yaffs_Device *)data;
- struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev);
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
unsigned long now = jiffies;
unsigned long next_dir_update = now;
unsigned long next_gc = now;
now = jiffies;
- if(time_after(now, next_dir_update)){
+ if(time_after(now, next_dir_update) && yaffs_bg_enable){
yaffs_UpdateDirtyDirectories(dev);
next_dir_update = now + HZ;
}
- if(time_after(now,next_gc)){
+ if(time_after(now,next_gc) && yaffs_bg_enable){
if(!dev->isCheckpointed){
urgency = yaffs_bg_gc_urgency(dev);
gcResult = yaffs_BackgroundGarbageCollect(dev, urgency);
static int yaffs_BackgroundStart(yaffs_Device *dev)
{
int retval = 0;
- struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev);
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+
+ if(dev->readOnly)
+ return -1;
context->bgRunning = 1;
static void yaffs_BackgroundStop(yaffs_Device *dev)
{
- struct yaffs_LinuxContext *ctxt = yaffs_DeviceToContext(dev);
+ struct yaffs_LinuxContext *ctxt = yaffs_DeviceToLC(dev);
ctxt->bgRunning = 0;
T(YAFFS_TRACE_OS,
(TSTR("yaffs_read_inode for %d\n"), (int)inode->i_ino));
- if(current != yaffs_DeviceToContext(dev)->readdirProcess)
+ if(current != yaffs_DeviceToLC(dev)->readdirProcess)
yaffs_GrossLock(dev);
obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
yaffs_FillInodeFromObject(inode, obj);
- if(current != yaffs_DeviceToContext(dev)->readdirProcess)
+ if(current != yaffs_DeviceToLC(dev)->readdirProcess)
yaffs_GrossUnlock(dev);
}
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_Device *dev = yaffs_SuperToDevice(sb);
-
- if (*flags & MS_RDONLY) {
- struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice;
-
- T(YAFFS_TRACE_OS,
- (TSTR("yaffs_remount_fs: %s: RO\n"), dev->name));
-
- yaffs_GrossLock(dev);
-
- yaffs_FlushSuperBlock(sb,1);
-
- if (mtd->sync)
- mtd->sync(mtd);
-
- yaffs_GrossUnlock(dev);
- } else {
- T(YAFFS_TRACE_OS,
- (TSTR("yaffs_remount_fs: %s: RW\n"), dev->name));
- }
-
- return 0;
-}
-#endif
-
static void yaffs_put_super(struct super_block *sb)
{
yaffs_Device *dev = yaffs_SuperToDevice(sb);
yaffs_FlushSuperBlock(sb,1);
- if (yaffs_DeviceToContext(dev)->putSuperFunc)
- yaffs_DeviceToContext(dev)->putSuperFunc(sb);
+ if (yaffs_DeviceToLC(dev)->putSuperFunc)
+ yaffs_DeviceToLC(dev)->putSuperFunc(sb);
yaffs_Deinitialise(dev);
yaffs_GrossUnlock(dev);
down(&yaffs_context_lock);
- ylist_del_init(&(yaffs_DeviceToContext(dev)->contextList));
+ ylist_del_init(&(yaffs_DeviceToLC(dev)->contextList));
up(&yaffs_context_lock);
- if (yaffs_DeviceToContext(dev)->spareBuffer) {
- YFREE(yaffs_DeviceToContext(dev)->spareBuffer);
- yaffs_DeviceToContext(dev)->spareBuffer = NULL;
+ if (yaffs_DeviceToLC(dev)->spareBuffer) {
+ YFREE(yaffs_DeviceToLC(dev)->spareBuffer);
+ yaffs_DeviceToLC(dev)->spareBuffer = NULL;
}
kfree(dev);
static void yaffs_MTDPutSuper(struct super_block *sb)
{
- struct mtd_info *mtd = yaffs_DeviceToContext(yaffs_SuperToDevice(sb))->mtd;
+ struct mtd_info *mtd = yaffs_DeviceToMtd(yaffs_SuperToDevice(sb));
if (mtd->sync)
mtd->sync(mtd);
static void yaffs_MarkSuperBlockDirty(yaffs_Device *dev)
{
- struct super_block *sb = yaffs_DeviceToContext(dev)->superBlock;
+ struct super_block *sb = yaffs_DeviceToLC(dev)->superBlock;
T(YAFFS_TRACE_OS, (TSTR("yaffs_MarkSuperBlockDirty() sb = %p\n"), sb));
if (sb)
struct yaffs_LinuxContext *context = NULL;
yaffs_DeviceParam *param;
+ int readOnly = 0;
+
yaffs_options options;
unsigned mount_id;
sb->s_op = &yaffs_super_ops;
sb->s_flags |= MS_NOATIME;
+ readOnly =((sb->s_flags & MS_RDONLY) != 0);
+
+
#ifdef YAFFS_COMPILE_EXPORTFS
sb->s_export_op = &yaffs_export_ops;
#endif
else if (!yaffs_devname(sb, devname_buf))
printk(KERN_INFO "yaffs: devname is NULL\n");
else
- printk(KERN_INFO "yaffs: dev is %d name is \"%s\"\n",
+ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n",
sb->s_dev,
- yaffs_devname(sb, devname_buf));
+ yaffs_devname(sb, devname_buf),
+ readOnly ? "ro" : "rw");
if (!data_str)
data_str = "";
* Set the yaffs_Device up for mtd
*/
+ if (!readOnly && !(mtd->flags & MTD_WRITEABLE)){
+ readOnly = 1;
+ printk(KERN_INFO "yaffs: mtd is read only, setting superblock read only");
+ sb->s_flags |= MS_RDONLY;
+ }
+
dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL);
context = kmalloc(sizeof(struct yaffs_LinuxContext),GFP_KERNEL);
param = &(dev->param);
memset(context,0,sizeof(struct yaffs_LinuxContext));
- dev->context = context;
+ dev->osContext = context;
YINIT_LIST_HEAD(&(context->contextList));
context->dev = dev;
context->superBlock = sb;
-
+ dev->readOnly = readOnly;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
sb->s_fs_info = dev;
sb->u.generic_sbp = dev;
#endif
- yaffs_DeviceToContext(dev)->mtd = mtd;
+ dev->driverContext = mtd;
param->name = mtd->name;
/* Set up the memory size parameters.... */
nandmtd2_ReadChunkWithTagsFromNAND;
param->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
param->queryNANDBlock = nandmtd2_QueryNANDBlock;
- yaffs_DeviceToContext(dev)->spareBuffer = YMALLOC(mtd->oobsize);
+ yaffs_DeviceToLC(dev)->spareBuffer = YMALLOC(mtd->oobsize);
param->isYaffs2 = 1;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
param->totalBytesPerChunk = mtd->writesize;
param->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
param->initialiseNAND = nandmtd_InitialiseNAND;
- yaffs_DeviceToContext(dev)->putSuperFunc = yaffs_MTDPutSuper;
+ yaffs_DeviceToLC(dev)->putSuperFunc = yaffs_MTDPutSuper;
param->markSuperBlockDirty = yaffs_MarkSuperBlockDirty;
param->gcControl = yaffs_gc_control_callback;
- yaffs_DeviceToContext(dev)->superBlock= sb;
+ yaffs_DeviceToLC(dev)->superBlock= sb;
#ifndef CONFIG_YAFFS_DOES_ECC
}
context->mount_id = mount_id;
- ylist_add_tail(&(yaffs_DeviceToContext(dev)->contextList), &yaffs_context_list);
+ ylist_add_tail(&(yaffs_DeviceToLC(dev)->contextList), &yaffs_context_list);
up(&yaffs_context_lock);
/* Directory search handling...*/
- YINIT_LIST_HEAD(&(yaffs_DeviceToContext(dev)->searchContexts));
+ YINIT_LIST_HEAD(&(yaffs_DeviceToLC(dev)->searchContexts));
param->removeObjectCallback = yaffs_RemoveObjectCallback;
- init_MUTEX(&(yaffs_DeviceToContext(dev)->grossLock));
+ init_MUTEX(&(yaffs_DeviceToLC(dev)->grossLock));
yaffs_GrossLock(dev);
buf += sprintf(buf, "allGCs............. %u\n", dev->allGCs);
buf += sprintf(buf, "passiveGCs......... %u\n", dev->passiveGCs);
buf += sprintf(buf, "oldestDirtyGCs..... %u\n", dev->oldestDirtyGCs);
+ buf += sprintf(buf, "nGCBlocks.......... %u\n", dev->nGCBlocks);
buf += sprintf(buf, "backgroundGCs...... %u\n", dev->backgroundGCs);
buf += sprintf(buf, "nRetriedWrites..... %u\n", dev->nRetriedWrites);
buf += sprintf(buf, "nRetireBlocks...... %u\n", dev->nRetiredBlocks);