#define YAFFS_COMPILE_EXPORTFS
#endif
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35))
+#define YAFFS_USE_SETATTR_COPY
+#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 YPROC_ROOT NULL
#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
+#define Y_INIT_TIMER(a) init_timer(a)
+#else
+#define Y_INIT_TIMER(a) init_timer_on_stack(a)
+#endif
+
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
#define WRITE_SIZE_STR "writesize"
#define WRITE_SIZE(mtd) ((mtd)->writesize)
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_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,
.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 unsigned yaffs_gc_control_callback(yaffs_Device *dev)
{
return yaffs_gc_control;
}
#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);
+
+ if(deleteme && obj){
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
+ yaffs_DeleteObject(obj);
+ yaffs_GrossUnlock(dev);
+ }
+ end_writeback(inode);
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
- /* If the object freeing was deferred, then the real
- * free happens now.
- * This should fix the inode inconsistency problem.
- */
+/* 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()
+ */
- yaffs_HandleDeferedFree(obj);
+static void yaffs_clear_inode(struct inode *inode)
+{
+ yaffs_Object *obj;
+ yaffs_Device *dev;
+
+ obj = yaffs_InodeToObject(inode);
+
+ 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));
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(time_before(expires,now))
expires = now + HZ;
- init_timer_on_stack(&timer);
+ Y_INIT_TIMER(&timer);
timer.expires = expires+1;
timer.data = (unsigned long) current;
timer.function = yaffs_background_waker;
int retval = 0;
struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+ if(dev->readOnly)
+ return -1;
+
context->bgRunning = 1;
context->bgThread = kthread_run(yaffs_BackgroundThread,
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);
context->dev = dev;
context->superBlock = sb;
-
+ dev->readOnly = readOnly;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
sb->s_fs_info = 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);