mtd/fs/jffs2/ecos/src file-ecos.c,1.6,1.7 fs-ecos.c,1.11,1.12
os-ecos.h,1.8,1.9
David Woodhouse
dwmw2 at infradead.org
Mon Nov 24 08:44:01 EST 2003
Update of /home/cvs/mtd/fs/jffs2/ecos/src
In directory phoenix.infradead.org:/tmp/cvs-serv994
Modified Files:
file-ecos.c fs-ecos.c os-ecos.h
Log Message:
Fix refcount leaks, remove all references to struct inode in core code
Index: file-ecos.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/ecos/src/file-ecos.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- file-ecos.c 20 Nov 2003 16:41:58 -0000 1.6
+++ file-ecos.c 24 Nov 2003 13:43:58 -0000 1.7
@@ -41,7 +41,6 @@
ClearPageError(pg);
}
- flush_dcache_page(pg);
kunmap(pg);
D1(printk(KERN_DEBUG "readpage finished\n"));
@@ -55,18 +54,6 @@
return ret;
}
-
-//int jffs2_readpage (struct file *filp, struct page *pg)
-int jffs2_readpage (struct inode *d_inode, struct page *pg)
-{
- // struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode);
- int ret;
-
- // down(&f->sem);
- ret = jffs2_do_readpage_unlock(d_inode, pg);
- // up(&f->sem);
- return ret;
-}
//int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end)
int jffs2_prepare_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end)
Index: fs-ecos.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/ecos/src/fs-ecos.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- fs-ecos.c 20 Nov 2003 16:41:58 -0000 1.11
+++ fs-ecos.c 24 Nov 2003 13:43:58 -0000 1.12
@@ -79,6 +79,9 @@
static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence);
+
+static int jffs2_read_inode (struct inode *inode);
+
//==========================================================================
// Filesystem table entries
@@ -160,25 +163,6 @@
//==========================================================================
// Ref count and nlink management
-// -------------------------------------------------------------------------
-// dec_refcnt()
-// Decrment the reference count on an inode. If this makes the ref count
-// zero, then this inode can be freed.
-
-static int dec_refcnt(struct inode *node)
-{
- int err = ENOERR;
- node->i_count--;
-
- // In JFFS2 inode's are temporary in ram structures that are free'd when the usage i_count drops to 0
- // The i_nlink however is managed by JFFS2 and is unrelated to usage
- if (node->i_count == 0) {
- // This inode is not in use, so delete it.
- iput(node);
- }
-
- return err;
-}
// FIXME: This seems like real cruft. Wouldn't it be better just to do the
// right thing?
@@ -221,6 +205,9 @@
{
D2(printf("init_dirsearch name = %s\n", name));
D2(printf("init_dirsearch dir = %x\n", dir));
+
+ /* Increase inode refcount since find_entry will decrease it again */
+ dir->i_count++;
ds->dir = dir;
ds->path = name;
ds->node = dir;
@@ -325,6 +312,8 @@
// Main interface to directory search code. This is used in all file
// level operations to locate the object named by the pathname.
+// Returns with use count incremented on both the sought object and
+// the directory it was found in.
static int jffs2_find(jffs2_dirsearch * d)
{
int err;
@@ -345,8 +334,8 @@
if (d->last)
return ENOERR;
- // every inode traversed in the find is temporary and should be free'd
- //iput(d->dir);
+ // Drop the directory we were using.
+ iput(d->dir);
// Update dirsearch object to search next directory.
d->dir = d->node;
@@ -438,7 +427,6 @@
static int jffs2_read_super(struct super_block *sb)
{
struct jffs2_sb_info *c;
- struct inode *root_i;
Cyg_ErrNo err;
cyg_uint32 len;
cyg_io_flash_getconfig_devsize_t ds;
@@ -477,19 +465,18 @@
return -err;
D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
- root_i = iget(sb, 1);
- if (is_bad_inode(root_i)) {
+ sb->s_root = iget(sb, 1);
+ if (!sb->s_root) {
D1(printk(KERN_WARNING "get root inode failed\n"));
- err = EIO;
+ err = -ENOMEM;
goto out_nodes;
}
-
- D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
- sb->s_root = d_alloc_root(root_i);
- if (!sb->s_root) {
- err = ENOMEM;
+ if (is_bad_inode(sb->s_root)) {
+ D1(printk(KERN_WARNING "get root inode failed\n"));
+ err = EIO;
goto out_root_i;
}
+
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = JFFS2_SUPER_MAGIC;
@@ -497,7 +484,7 @@
return 0;
out_root_i:
- iput(root_i);
+ iput(sb->s_root);
out_nodes:
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
@@ -508,7 +495,7 @@
static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte)
{
- extern cyg_mtab_entry cyg_mtab[], cyg_mtab_end;
+ extern cyg_mtab_entry mtab[], mtab_end;
struct super_block *jffs2_sb = NULL;
struct jffs2_sb_info *c;
cyg_mtab_entry *m;
@@ -524,10 +511,10 @@
// Iterate through the mount table to see if we're mounted
// FIXME: this should be done better - perhaps if the superblock
// can be stored as an inode in the icache.
- for (m = &cyg_mtab[0]; m != &cyg_mtab_end; m++) {
+ for (m = &mtab[0]; m != &mtab_end; m++) {
// stop if there are more than the configured maximum
- if (m - &cyg_mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) {
- m = &cyg_mtab_end;
+ if (m - &mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) {
+ m = &mtab_end;
break;
}
if (m->valid && strcmp(m->fsname, "jffs2") == 0 &&
@@ -593,10 +580,18 @@
// Only really umount if this is the only mount
if (jffs2_sb->s_mount_count == 1) {
- if (root->i_cache_next != NULL) // root icount was set to 1 on mount
+ if (root->i_cache_next != NULL) {
+ struct inode *inode = root->i_cache_next;
+ printf("Refuse to unmount.\n");
+ while (inode) {
+ printf("Ino #%u has use count %d\n",
+ inode->i_ino, inode->i_count);
+ inode = inode->i_cache_next;
+ }
+ // root icount was set to 1 on mount
return EBUSY;
-
- dec_refcnt(root); // Time to free the root inode
+ }
+ iput(root); // Time to free the root inode
//Clear root inode
//root_i = NULL;
@@ -677,13 +672,26 @@
// The node exists. If the O_CREAT and O_EXCL bits are set, we
// must fail the open.
- if ((mode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ if ((mode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
+ iput(ds.node);
err = EEXIST;
- else
+ } else
node = ds.node;
}
- if (err == ENOERR && (mode & O_TRUNC)) {
+ // Finished with the directory now
+ iput(ds.dir);
+
+ if (err != ENOERR)
+ return err;
+
+ // Check that we actually have a file here
+ if (S_ISDIR(node->i_mode)) {
+ iput(node);
+ return EISDIR;
+ }
+
+ if (mode & O_TRUNC) {
struct jffs2_inode_info *f = JFFS2_INODE_INFO(node);
struct jffs2_sb_info *c = JFFS2_SB_INFO(node->i_sb);
// If the O_TRUNC bit is set we must clean out the file data.
@@ -694,17 +702,7 @@
node->i_ctime = node->i_mtime = cyg_timestamp();
}
- if (err != ENOERR)
- return err;
-
- // Check that we actually have a file here
- if (S_ISDIR(node->i_mode))
- return EISDIR;
-
- node->i_count++; // Count successful open
-
- // Initialize the file object
-
+ // Initialise the file object
file->f_flag |= mode & CYG_FILE_MODE_MASK;
file->f_type = CYG_FILE_TYPE_FILE;
file->f_ops = &jffs2_fileops;
@@ -736,12 +734,17 @@
err = jffs2_find(&ds);
- if (err != ENOERR)
+ if (err != ENOERR) {
+ iput(ds.dir);
return err;
+ }
// Cannot unlink directories, use rmdir() instead
- if (S_ISDIR(ds.node->i_mode))
+ if (S_ISDIR(ds.node->i_mode)) {
+ iput(ds.dir);
+ iput(ds.node);
return EPERM;
+ }
// Delete it from its directory
@@ -759,8 +762,10 @@
this.hash = end_name_hash(hash);
err = jffs2_unlink(ds.dir, ds.node, &this);
+ iput(ds.dir);
+ iput(ds.node);
- return err;
+ return -err;
}
// -------------------------------------------------------------------------
@@ -803,22 +808,18 @@
this.len = hashname - (const char *) this.name;
this.hash = end_name_hash(hash);
- err = jffs2_mkdir(ds.dir, &this, 0, &node);
-
- if (err != 0)
- return ENOSPC;
-
+ err = -jffs2_mkdir(ds.dir, &this, 0, &node);
}
- // If this was not the last element, then and intermediate
+ // If this was not the last element, then an intermediate
// directory does not exist.
} else {
// If there we no error, something already exists with that
// name, so we cannot create another one.
-
+ iput(ds.node);
if (err == ENOERR)
err = EEXIST;
}
-
+ iput(ds.dir);
return err;
}
@@ -843,12 +844,17 @@
err = jffs2_find(&ds);
- if (err != ENOERR)
+ if (err != ENOERR) {
+ iput(ds.dir);
return err;
+ }
// Check that this is actually a directory.
- if (!S_ISDIR(ds.node->i_mode))
+ if (!S_ISDIR(ds.node->i_mode)) {
+ iput(ds.dir);
+ iput(ds.node);
return EPERM;
+ }
// Delete the entry.
hashname = ds.name;
@@ -866,9 +872,9 @@
err = jffs2_rmdir(ds.dir, ds.node, &this);
- return err;
-
- return ENOERR;
+ iput(ds.dir);
+ iput(ds.node);
+ return -err;
}
// -------------------------------------------------------------------------
@@ -891,23 +897,33 @@
err = jffs2_find(&ds1);
- if (err != ENOERR)
+ if (err != ENOERR) {
+ iput(ds1.dir);
return err;
+ }
init_dirsearch(&ds2, (struct inode *) dir2, name2);
err = jffs2_find(&ds2);
// Allow through renames to non-existent objects.
- if (ds2.last && err == ENOENT)
- ds2.node = NULL, err = ENOERR;
+ if (ds2.last && err == ENOENT) {
+ ds2.node = NULL;
+ err = ENOERR;
+ }
- if (err != ENOERR)
+ if (err != ENOERR) {
+ iput(ds1.dir);
+ iput(ds1.node);
+ iput(ds2.dir);
return err;
+ }
// Null rename, just return
- if (ds1.node == ds2.node)
- return ENOERR;
+ if (ds1.node == ds2.node) {
+ err = ENOERR;
+ goto out;
+ }
hashname = ds1.name;
this1.name = hashname;
@@ -939,33 +955,43 @@
if (ds2.node) {
// Check that we are renaming like-for-like
- if (!S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode))
- return EISDIR;
+ if (!S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode)) {
+ err = EISDIR;
+ goto out;
+ }
- if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode))
- return ENOTDIR;
+ if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode)) {
+ err = ENOTDIR;
+ goto out;
+ }
// Now delete the destination directory entry
- err = jffs2_unlink(ds2.dir, ds2.node, &this2);
+ err = -jffs2_unlink(ds2.dir, ds2.node, &this2);
if (err != 0)
- return err;
+ goto out;
}
// Now we know that there is no clashing node at the destination,
// make a new direntry at the destination and delete the old entry
// at the source.
- err = jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2);
+ err = -jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2);
// Update directory times
- if (err == 0)
+ if (!err)
ds1.dir->i_ctime =
ds1.dir->i_mtime =
ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
-
- return err;
+ out:
+ iput(ds1.dir);
+ iput(ds1.node);
+ iput(ds2.dir);
+ if (ds2.node)
+ iput(ds2.node);
+
+ return -err;
}
// -------------------------------------------------------------------------
@@ -992,23 +1018,37 @@
err = jffs2_find(&ds1);
- if (err != ENOERR)
+ if (err != ENOERR) {
+ iput(ds1.dir);
return err;
+ }
init_dirsearch(&ds2, (struct inode *) dir2, name2);
err = jffs2_find(&ds2);
// Don't allow links to existing objects
- if (err == ENOERR)
+ if (err == ENOERR) {
+ iput(ds1.dir);
+ iput(ds1.node);
+ iput(ds2.dir);
+ iput(ds2.node);
return EEXIST;
+ }
// Allow through links to non-existing terminal objects
- if (ds2.last && err == ENOENT)
- ds2.node = NULL, err = ENOERR;
+ if (ds2.last && err == ENOENT) {
+ iput(ds2.node);
+ ds2.node = NULL;
+ err = ENOERR;
+ }
- if (err != ENOERR)
+ if (err != ENOERR) {
+ iput(ds1.dir);
+ iput(ds1.node);
+ iput(ds2.dir);
return err;
+ }
// Now we know that there is no existing node at the destination,
// make a new direntry at the destination.
@@ -1032,7 +1072,11 @@
ds1.node->i_ctime =
ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
- return err;
+ iput(ds1.dir);
+ iput(ds1.node);
+ iput(ds2.dir);
+
+ return -err;
}
// -------------------------------------------------------------------------
@@ -1053,14 +1097,16 @@
err = jffs2_find(&ds);
+ iput(ds.dir);
+
if (err != ENOERR)
return err;
// check it is really a directory.
- if (!S_ISDIR(ds.node->i_mode))
+ if (!S_ISDIR(ds.node->i_mode)) {
+ iput(ds.node);
return ENOTDIR;
-
- ds.node->i_count++; // Count successful open
+ }
// Initialize the file object, setting the f_ops field to a
// special set of file ops.
@@ -1096,6 +1142,8 @@
init_dirsearch(&ds, (struct inode *) dir, name);
err = jffs2_find(&ds);
+ iput(ds.dir);
+ iput((struct inode *)dir);
if (err != ENOERR)
return err;
@@ -1104,10 +1152,6 @@
if (!S_ISDIR(ds.node->i_mode))
return ENOTDIR;
- // Increment ref count to keep this directory in existance
- // while it is the current cdir.
- ds.node->i_count++;
-
// Pass it out
*dir_out = (cyg_dir) ds.node;
} else {
@@ -1118,7 +1162,7 @@
struct inode *node = (struct inode *) dir;
// Just decrement directory reference count.
- dec_refcnt(node);
+ iput(node);
}
return ENOERR;
@@ -1141,6 +1185,7 @@
init_dirsearch(&ds, (struct inode *) dir, name);
err = jffs2_find(&ds);
+ iput(ds.dir);
if (err != ENOERR)
return err;
@@ -1157,7 +1202,7 @@
buf->st_mtime = ds.node->i_mtime;
buf->st_ctime = ds.node->i_ctime;
- return err;
+ iput(ds.node);
return ENOERR;
}
@@ -1179,6 +1224,7 @@
init_dirsearch(&ds, (struct inode *) dir, name);
err = jffs2_find(&ds);
+ iput(ds.dir);
if (err != ENOERR)
return err;
@@ -1191,9 +1237,9 @@
default:
err = EINVAL;
}
- return err;
- return ENOERR;
+ iput(ds.node);
+ return err;
}
// -------------------------------------------------------------------------
@@ -1445,7 +1491,7 @@
D2(printf("jffs2_fo_close\n"));
- dec_refcnt(node);
+ iput(node);
fp->f_data = 0; // zero data pointer
@@ -1630,7 +1676,7 @@
//
//==========================================================================
-struct page *read_cache_page(unsigned long index,
+static struct page *read_cache_page(unsigned long index,
int (*filler) (void *, struct page *), void *data)
{
// Only called in gc.c jffs2_garbage_collect_dnode
@@ -1656,7 +1702,7 @@
return gc_page;
}
-void page_cache_release(struct page *pg)
+static void page_cache_release(struct page *pg)
{
// Only called in gc.c jffs2_garbage_collect_dnode
@@ -1666,7 +1712,34 @@
free(pg);
}
-struct inode *new_inode(struct super_block *sb)
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f,
+ unsigned long offset,
+ unsigned long *priv)
+{
+ struct inode *inode = OFNI_EDONI_2SFFJ(f);
+ struct page *pg;
+
+ pg = read_cache_page(offset >> PAGE_CACHE_SHIFT,
+ (void *)jffs2_do_readpage_unlock, inode);
+ if (IS_ERR(pg))
+ return (void *)pg;
+
+ *priv = (unsigned long)pg;
+ return kmap(pg);
+}
+
+void jffs2_gc_release_page(struct jffs2_sb_info *c,
+ unsigned char *ptr,
+ unsigned long *priv)
+{
+ struct page *pg = (void *)*priv;
+
+ kunmap(pg);
+ page_cache_release(pg);
+}
+
+static struct inode *new_inode(struct super_block *sb)
{
// Only called in write.c jffs2_new_inode
@@ -1723,10 +1796,6 @@
struct inode *iget(struct super_block *sb, cyg_uint32 ino)
{
-
- // Substitute for iget drops straight through to reading the
- // inode from disk if it is not in the inode cache
-
// Called in super.c jffs2_read_super, dir.c jffs2_lookup,
// and gc.c jffs2_garbage_collect_pass
@@ -1747,29 +1816,39 @@
return 0;
inode->i_ino = ino;
- jffs2_read_inode(inode);
inode->i_count = 1;
+ if (jffs2_read_inode(inode)) {
+ printf("jffs2_read_inode() failed\n");
+ iput(inode);
+ inode = NULL;
+ }
return inode;
}
+// -------------------------------------------------------------------------
+// Decrement the reference count on an inode. If this makes the ref count
+// zero, then this inode can be freed.
+
void iput(struct inode *i)
{
- // Called in dec_refcnt, jffs2_find
+ // Called in jffs2_find
// (and jffs2_open and jffs2_ops_mkdir?)
// super.c jffs2_read_super,
// and gc.c jffs2_garbage_collect_pass
struct inode *cached_inode;
- D2(printf
- ("free iput inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", i));
- if (i && i->i_count) {
- /* Added by dwmw2. iget/iput in Linux track the use count,
- don't just unconditionally free it */
- printf("iput called for used inode\n");
- return;
+ if (!i) {
+ printf("iput() called with NULL inode\n");
+ // and let it fault...
}
+
+ i->i_count--;
+
+ if (i->i_count)
+ return;
+
if (i != NULL) {
// Remove from the icache
for (cached_inode = i->i_sb->s_root; cached_inode != NULL;
@@ -2026,13 +2105,11 @@
inode->i_size = 0;
- insert_inode_hash(inode);
-
return inode;
}
-void jffs2_read_inode (struct inode *inode)
+static int jffs2_read_inode (struct inode *inode)
{
struct jffs2_inode_info *f;
struct jffs2_sb_info *c;
@@ -2051,7 +2128,7 @@
if (ret) {
make_bad_inode(inode);
up(&f->sem);
- return;
+ return ret;
}
inode->i_mode = jemode_to_cpu(latest_node.mode);
inode->i_uid = je16_to_cpu(latest_node.uid);
@@ -2065,4 +2142,76 @@
up(&f->sem);
D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
+ return 0;
}
+
+
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f)
+{
+ iput(OFNI_EDONI_2SFFJ(f));
+}
+
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+ int inum, int nlink)
+{
+ struct inode *inode;
+ struct jffs2_inode_cache *ic;
+ if (!nlink) {
+ /* The inode has zero nlink but its nodes weren't yet marked
+ obsolete. This has to be because we're still waiting for
+ the final (close() and) iput() to happen.
+
+ There's a possibility that the final iput() could have
+ happened while we were contemplating. In order to ensure
+ that we don't cause a new read_inode() (which would fail)
+ for the inode in question, we use ilookup() in this case
+ instead of iget().
+
+ The nlink can't _become_ zero at this point because we're
+ holding the alloc_sem, and jffs2_do_unlink() would also
+ need that while decrementing nlink on any inode.
+ */
+ inode = ilookup(OFNI_BS_2SFFJ(c), inum);
+ if (!inode) {
+ D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
+ inum));
+
+ spin_lock(&c->inocache_lock);
+ ic = jffs2_get_ino_cache(c, inum);
+ if (!ic) {
+ D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
+ spin_unlock(&c->inocache_lock);
+ return NULL;
+ }
+ if (ic->state != INO_STATE_CHECKEDABSENT) {
+ /* Wait for progress. Don't just loop */
+ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
+ ic->ino, ic->state));
+ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+ } else {
+ spin_unlock(&c->inocache_lock);
+ }
+
+ return NULL;
+ }
+ } else {
+ /* Inode has links to it still; they're not going away because
+ jffs2_do_unlink() would need the alloc_sem and we have it.
+ Just iget() it, and if read_inode() is necessary that's OK.
+ */
+ inode = iget(OFNI_BS_2SFFJ(c), inum);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ }
+ if (is_bad_inode(inode)) {
+ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
+ inum, nlink);
+ /* NB. This will happen again. We need to do something appropriate here. */
+ iput(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ return JFFS2_INODE_INFO(inode);
+}
+
Index: os-ecos.h
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/ecos/src/os-ecos.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- os-ecos.h 20 Nov 2003 16:41:58 -0000 1.8
+++ os-ecos.h 24 Nov 2003 13:43:58 -0000 1.9
@@ -228,6 +228,21 @@
//int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
int jffs2_commit_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end);
+/* fs.c */
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f);
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+ int inum, int nlink);
+
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f,
+ unsigned long offset,
+ unsigned long *priv);
+void jffs2_gc_release_page(struct jffs2_sb_info *c,
+ unsigned char *pg,
+ unsigned long *priv);
+
+
#ifndef CONFIG_JFFS2_FS_NAND
#define jffs2_can_mark_obsolete(c) (1)
#define jffs2_cleanmarker_oob(c) (0)
@@ -248,7 +263,6 @@
#endif
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
void jffs2_clear_inode (struct inode *inode);
-void jffs2_read_inode (struct inode *inode);
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
{
More information about the linux-mtd-cvs
mailing list