+ yaffs_GrossLock(dev);
+
+ buf->f_type = YAFFS_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_namelen = 255;
+
+ if (dev->nDataBytesPerChunk & (dev->nDataBytesPerChunk - 1)) {
+ /* Do this if chunk size is not a power of 2 */
+
+ uint64_t bytesInDev;
+ uint64_t bytesFree;
+
+ 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;
+
+ bytesFree = ((uint64_t)(yaffs_GetNumberOfFreeChunks(dev))) *
+ ((uint64_t)(dev->nDataBytesPerChunk));
+
+ do_div(bytesFree, sb->s_blocksize);
+
+ buf->f_bfree = bytesFree;
+
+ } else if (sb->s_blocksize > dev->nDataBytesPerChunk) {
+
+ buf->f_blocks =
+ (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->param.endBlock - dev->param.startBlock + 1) *
+ dev->param.nChunksPerBlock *
+ (dev->nDataBytesPerChunk / sb->s_blocksize);
+
+ buf->f_bfree =
+ yaffs_GetNumberOfFreeChunks(dev) *
+ (dev->nDataBytesPerChunk / sb->s_blocksize);
+ }
+
+ buf->f_files = 0;
+ buf->f_ffree = 0;
+ buf->f_bavail = buf->f_bfree;
+
+ yaffs_GrossUnlock(dev);
+ return 0;
+}
+
+
+
+static void yaffs_FlushInodes(struct super_block *sb)
+{
+ struct inode *iptr;
+ yaffs_Object *obj;
+
+ list_for_each_entry(iptr,&sb->s_inodes, i_sb_list){
+ obj = yaffs_InodeToObject(iptr);
+ if(obj){
+ T(YAFFS_TRACE_OS, (TSTR("flushing obj %d\n"),
+ obj->objectId));
+ yaffs_FlushFile(obj,1,0);
+ }
+ }
+}
+
+
+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 unsigned yaffs_bg_gc_urgency(yaffs_Device *dev)
+{
+ unsigned erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+ unsigned scatteredFree = 0; /* Free chunks not in an erased block */
+
+ if(erasedChunks < dev->nFreeChunks)
+ scatteredFree = (dev->nFreeChunks - erasedChunks);
+
+ if(!context->bgRunning)
+ return 0;
+ else if(scatteredFree < (dev->param.nChunksPerBlock * 2))
+ return 0;
+ else if(erasedChunks > dev->nFreeChunks/2)
+ return 0;
+ else if(erasedChunks > dev->nFreeChunks/4)
+ return 1;
+ else
+ return 2;
+}
+
+static int yaffs_do_sync_fs(struct super_block *sb,
+ int request_checkpoint)
+{
+
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4);
+ unsigned gc_urgent = yaffs_bg_gc_urgency(dev);
+ int do_checkpoint;
+
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs_do_sync_fs: gc-urgency %d %s %s%s\n"),
+ gc_urgent,
+ sb->s_dirt ? "dirty" : "clean",
+ request_checkpoint ? "checkpoint requested" : "no checkpoint",
+ oneshot_checkpoint ? " one-shot" : "" ));
+
+ yaffs_GrossLock(dev);
+ do_checkpoint = ((request_checkpoint && !gc_urgent) ||
+ oneshot_checkpoint) &&
+ !dev->isCheckpointed;
+
+ if (sb->s_dirt || do_checkpoint) {
+ yaffs_FlushSuperBlock(sb, !dev->isCheckpointed && do_checkpoint);
+ sb->s_dirt = 0;
+ if(oneshot_checkpoint)
+ yaffs_auto_checkpoint &= ~4;
+ }
+ yaffs_GrossUnlock(dev);
+
+ 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
+
+void yaffs_background_waker(unsigned long data)
+{
+ wake_up_process((struct task_struct *)data);
+}
+
+static int yaffs_BackgroundThread(void *data)
+{
+ yaffs_Device *dev = (yaffs_Device *)data;
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+ unsigned long now = jiffies;
+ unsigned long next_dir_update = now;
+ unsigned long next_gc = now;
+ unsigned long expires;
+ unsigned int urgency;
+
+ int gcResult;
+ struct timer_list timer;
+
+ T(YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs_background starting for dev %p\n"),
+ (void *)dev));
+
+ set_freezable();
+
+ while(context->bgRunning){
+ T(YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs_background\n")));
+
+ if(kthread_should_stop())
+ break;
+
+ if(try_to_freeze())
+ continue;
+
+ yaffs_GrossLock(dev);
+
+ now = jiffies;
+
+ if(time_after(now, next_dir_update)){
+ yaffs_UpdateDirtyDirectories(dev);
+ next_dir_update = now + HZ;
+ }
+
+ if(time_after(now,next_gc)){
+ if(!dev->isCheckpointed){
+ urgency = yaffs_bg_gc_urgency(dev);
+ gcResult = yaffs_BackgroundGarbageCollect(dev, urgency);
+ if(urgency > 1)
+ next_gc = now + HZ/20+1;
+ else if(urgency > 0)
+ next_gc = now + HZ/10+1;
+ else
+ next_gc = now + HZ * 2;
+ } else /*
+ * gc not running so set to next_dir_update
+ * to cut down on wake ups
+ */
+ next_gc = next_dir_update;
+ }
+ yaffs_GrossUnlock(dev);
+#if 1
+ expires = next_dir_update;
+ if (time_before(next_gc,expires))
+ expires = next_gc;
+ if(time_before(expires,now))
+ expires = now + HZ;
+
+ init_timer_on_stack(&timer);
+ timer.expires = expires+1;
+ timer.data = (unsigned long) current;
+ timer.function = yaffs_background_waker;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_timer(&timer);
+ schedule();
+ del_timer_sync(&timer);
+#else
+ msleep(10);
+#endif
+ }
+
+ return 0;
+}
+
+static int yaffs_BackgroundStart(yaffs_Device *dev)
+{
+ int retval = 0;
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+
+ context->bgRunning = 1;
+
+ context->bgThread = kthread_run(yaffs_BackgroundThread,
+ (void *)dev,"yaffs-bg-%d",context->mount_id);
+
+ 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_DeviceToLC(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)
+#else
+static int yaffs_write_super(struct super_block *sb)
+#endif
+{
+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2);
+
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs_write_super%s\n"),
+ request_checkpoint ? " checkpt" : ""));
+
+ yaffs_do_sync_fs(sb, request_checkpoint);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ return 0;
+#endif
+}
+
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
+static int yaffs_sync_fs(struct super_block *sb, int wait)
+#else
+static int yaffs_sync_fs(struct super_block *sb)
+#endif
+{
+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1);
+
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC,
+ (TSTR("yaffs_sync_fs%s\n"),
+ request_checkpoint ? " checkpt" : ""));
+
+ yaffs_do_sync_fs(sb, request_checkpoint);
+
+ return 0;
+}
+
+#ifdef YAFFS_USE_OWN_IGET
+
+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino)
+{
+ struct inode *inode;
+ yaffs_Object *obj;
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_iget for %lu\n"), ino));
+
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ /* NB This is called as a side effect of other functions, but
+ * we had to release the lock to prevent deadlocks, so
+ * need to lock again.
+ */
+
+ yaffs_GrossLock(dev);
+
+ obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
+
+ yaffs_FillInodeFromObject(inode, obj);
+
+ yaffs_GrossUnlock(dev);
+
+ unlock_new_inode(inode);
+ return inode;
+}
+
+#else
+
+static void yaffs_read_inode(struct inode *inode)
+{
+ /* NB This is called as a side effect of other functions, but
+ * we had to release the lock to prevent deadlocks, so
+ * need to lock again.
+ */
+
+ yaffs_Object *obj;
+ yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb);
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_read_inode for %d\n"), (int)inode->i_ino));
+
+ if(current != yaffs_DeviceToLC(dev)->readdirProcess)
+ yaffs_GrossLock(dev);
+
+ obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
+
+ yaffs_FillInodeFromObject(inode, obj);
+
+ if(current != yaffs_DeviceToLC(dev)->readdirProcess)
+ yaffs_GrossUnlock(dev);
+}
+
+#endif
+
+static YLIST_HEAD(yaffs_context_list);
+struct semaphore yaffs_context_lock;
+
+static void yaffs_put_super(struct super_block *sb)
+{
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+
+ T(YAFFS_TRACE_OS, (TSTR("yaffs_put_super\n")));
+
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND,
+ (TSTR("Shutting down yaffs background thread\n")));
+ yaffs_BackgroundStop(dev);
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs background thread shut down\n")));
+
+ yaffs_GrossLock(dev);
+
+ yaffs_FlushSuperBlock(sb,1);
+
+ if (yaffs_DeviceToLC(dev)->putSuperFunc)
+ yaffs_DeviceToLC(dev)->putSuperFunc(sb);
+
+
+ yaffs_Deinitialise(dev);
+
+ yaffs_GrossUnlock(dev);
+
+ down(&yaffs_context_lock);
+ ylist_del_init(&(yaffs_DeviceToLC(dev)->contextList));
+ up(&yaffs_context_lock);
+
+ 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_DeviceToMtd(yaffs_SuperToDevice(sb));
+
+ if (mtd->sync)
+ mtd->sync(mtd);
+
+ put_mtd_device(mtd);
+}
+
+
+static void yaffs_MarkSuperBlockDirty(yaffs_Device *dev)
+{
+ struct super_block *sb = yaffs_DeviceToLC(dev)->superBlock;
+
+ T(YAFFS_TRACE_OS, (TSTR("yaffs_MarkSuperBlockDirty() sb = %p\n"), sb));
+ if (sb)
+ sb->s_dirt = 1;
+}
+
+typedef struct {
+ int inband_tags;
+ 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 30
+static int yaffs_parse_options(yaffs_options *options, const char *options_str)
+{
+ char cur_opt[MAX_OPT_LEN + 1];
+ int p;
+ int error = 0;
+
+ /* Parse through the options which is a comma seperated list */
+
+ while (options_str && *options_str && !error) {
+ 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;
+ p++;
+ }
+ options_str++;
+ }
+
+ if (!strcmp(cur_opt, "inband-tags"))
+ options->inband_tags = 1;
+ 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;
+ else if (!strcmp(cur_opt, "no-checkpoint-write"))
+ options->skip_checkpoint_write = 1;
+ else if (!strcmp(cur_opt, "no-checkpoint")) {
+ options->skip_checkpoint_read = 1;
+ options->skip_checkpoint_write = 1;
+ } else {
+ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n",
+ cur_opt);
+ error = 1;
+ }
+ }
+
+ return error;
+}
+
+static struct super_block *yaffs_internal_read_super(int yaffsVersion,
+ struct super_block *sb,
+ void *data, int silent)
+{
+ int nBlocks;
+ struct inode *inode = NULL;
+ struct dentry *root;
+ yaffs_Device *dev = 0;
+ char devname_buf[BDEVNAME_SIZE + 1];
+ struct mtd_info *mtd;
+ int err;
+ char *data_str = (char *)data;
+ struct yaffs_LinuxContext *context = NULL;
+ yaffs_DeviceParam *param;
+
+ yaffs_options options;
+
+ unsigned mount_id;
+ int found;
+ struct yaffs_LinuxContext *context_iterator;
+ struct ylist_head *l;
+
+ sb->s_magic = YAFFS_MAGIC;
+ sb->s_op = &yaffs_super_ops;
+ sb->s_flags |= MS_NOATIME;
+
+#ifdef YAFFS_COMPILE_EXPORTFS
+ sb->s_export_op = &yaffs_export_ops;
+#endif
+
+ if (!sb)