BUG() in clear_inode() when unlinking file in jffs

From: Alexander Larsson (alex@cendio.se)
Date: Fri May 12 2000 - 11:01:11 EST


I'm porting Axis journaling flash file system (jffs) to 2.3.x, and I'm
currently having a nasty problem i can't figure out.

When i unlink a file that i have recently read i get :
May 12 13:00:53 tammy kernel: kernel BUG at inode.c:269!
May 12 13:00:53 tammy kernel: invalid operand: 0000
May 12 13:00:53 tammy kernel: CPU: 0

This is with 2.3.99-pre3, so the code in question looks like this:
void clear_inode(struct inode *inode)
{
        if (inode->i_data.nrpages)
                BUG();

mount, ls, rm file, umount works fine.
mount, ls, cat file, unmount also works, but
mount, ls, cat file, rm file shows the BUG (rm segfaults)

It seems there are some data left in the page-cache when the inode is
being destroyed. I can't for the life of me figure out why this is so.
Shouldn't the data pages be removed when i do d_delete(dentry) in unlink?

The readpage code looks like this: (Almost exactly like cramfs)
/* Try to read a page of data from a file. */
static int
jffs_readpage(struct dentry *dentry, struct page *page)
{
        unsigned long buf;
        unsigned long read_len;
        int result = -EIO;
        struct inode *inode = (struct inode*)page->mapping->host;
        struct jffs_file *f = (struct jffs_file *)inode->u.generic_ip;
        int r;
        loff_t offset;

        D2(printk("***jffs_readpage(): file = \"%s\", page->offset = %lu\n",
                  (f->name ? f->name : ""), (long)offset));

        get_page(page);
        /* Don't LockPage(page), should be locked already */
        buf = page_address(page);
        ClearPageUptodate(page);
        ClearPageError(page);

        offset = page->index << PAGE_CACHE_SHIFT;
        if (offset < inode->i_size) {
                read_len = jffs_min(inode->i_size - offset, PAGE_SIZE);
                r = jffs_read_data(f, (char *)buf, offset, read_len);
                if (r == read_len) {
                        if (read_len < PAGE_SIZE) {
                                memset((void *)(buf + read_len), 0,
                                       PAGE_SIZE - read_len);
                        }
                        SetPageUptodate(page);
                        result = 0;
                }
                D(else {
                        printk("***jffs_readpage(): Read error! "
                               "Wanted to read %lu bytes but only "
                               "read %d bytes.\n", read_len, r);
                });
        }
        if (result) {
                memset((void *)buf, 0, PAGE_SIZE);
                SetPageError(page);
        }

        UnlockPage(page);
        
        put_page(page);
        
        D3(printk("jffs_readpage(): Leaving...\n"));

        return result;
} /* jffs_readpage() */

unlink looks like this: (A bit more code, but the freeing stuff looks like
most other fs's, including ext2)
/* Remove any kind of file except for directories. */
static int
jffs_unlink(struct inode *dir, struct dentry *dentry)
{
        D3(printk("***jffs_unlink()\n"));
        return jffs_remove(dir, dentry, 0);
}

/* Remove a JFFS entry, i.e. plain files, directories, etc. Here we
   shouldn't test for free space on the device. */
static int
jffs_remove(struct inode *dir, struct dentry *dentry, int type)
{
        struct jffs_raw_inode raw_inode;
        struct jffs_control *c;
        struct jffs_file *dir_f; /* The file-to-remove's parent. */
        struct jffs_file *del_f; /* The file to remove. */
        struct jffs_node *del_node;
        struct inode *inode = 0;
        int result = 0;

        D1({
                int len = dentry->d_name.len;
                const char *name = dentry->d_name.name;
                char *_name = (char *) kmalloc(len + 1, GFP_KERNEL);
                memcpy(_name, name, len);
                _name[len] = '\0';
                printk("***jffs_remove(): file = \"%s\"\n", _name);
                kfree(_name);
        });

        result = -ENAMETOOLONG;
        if (dentry->d_name.len > JFFS_MAX_NAME_LEN)
                goto jffs_remove_end;

        dir_f = (struct jffs_file *) dir->u.generic_ip;
        c = dir_f->c;

        result = -ENOENT;
        if (!(del_f = jffs_find_child(dir_f, dentry->d_name.name,
                                      dentry->d_name.len))) {
                D(printk("jffs_remove(): jffs_find_child() failed.\n"));
                goto jffs_remove_end;
        }

        if (S_ISDIR(type)) {
                if (del_f->children) {
                        result = -ENOTEMPTY;
                        goto jffs_remove_end;
                }
        } else if (S_ISDIR(del_f->mode)) {
                D(printk("jffs_remove(): node is a directory "
                         "but it shouldn't be.\n"));
                result = -EPERM;
                goto jffs_remove_end;
        }

        inode = dentry->d_inode;
        DQUOT_INIT(inode);
        
        result = -EIO;
        if (del_f->ino != inode->i_ino)
                goto jffs_remove_end;

        if (!inode->i_nlink) {
                printk("Deleting nonexistent file inode: %lu, nlink:%d\n",
                       inode->i_ino, inode->i_nlink);
                inode->i_nlink=1;
        }

        /* Create a node for the deletion. */
        result = -ENOMEM;
        if (!(del_node = (struct jffs_node *)
                         kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) {
                D(printk("jffs_remove(): Allocation failed!\n"));
                goto jffs_remove_end;
        }
        DJM(no_jffs_node++);
        del_node->data_offset = 0;
        del_node->removed_size = 0;

        /* Initialize the raw inode. */
        raw_inode.magic = JFFS_MAGIC_BITMASK;
        raw_inode.ino = del_f->ino;
        raw_inode.pino = del_f->pino;
        raw_inode.version = del_f->highest_version + 1;
        raw_inode.mode = del_f->mode;
        raw_inode.uid = current->fsuid;
        raw_inode.gid = current->fsgid;
        raw_inode.atime = CURRENT_TIME;
        raw_inode.mtime = del_f->mtime;
        raw_inode.ctime = raw_inode.atime;
        raw_inode.offset = 0;
        raw_inode.dsize = 0;
        raw_inode.rsize = 0;
        raw_inode.nsize = 0;
        raw_inode.nlink = del_f->nlink;
        raw_inode.spare = 0;
        raw_inode.rename = 0;
        raw_inode.deleted = 1;

        /* Write the new node to the flash memory. */
        if (jffs_write_node(c, del_node, &raw_inode, 0, 0) < 0) {
                kfree(del_node);
                DJM(no_jffs_node--);
                result = -EIO;
                goto jffs_remove_end;
        }

        /* Update the file. This operation will make the file disappear
           from the in-memory file system structures. */
        jffs_insert_node(c, del_f, &raw_inode, 0, del_node);

        dir->i_version = ++event;
        dir->i_ctime = dir->i_mtime = CURRENT_TIME;
        mark_inode_dirty(dir);
        inode->i_nlink--;
        if (inode->i_nlink == 0) {
                inode->u.generic_ip = 0;
        }
        inode->i_ctime = dir->i_ctime;
        mark_inode_dirty(inode);

        d_delete(dentry); /* This also frees the inode */

        result = 0;
jffs_remove_end:
        return result;
} /* jffs_remove() */

The rest of the code is availible for anon cvs. Just do:
cvs -d :pserver:anoncvs@infradead.org:/home/cvs login (password: anoncvs)
cvs -d :pserver:anoncvs@infradead.org:/home/cvs co mtd

The file in question is fs/jffs/inode.c.
    
I've completely run out of ideas on this one. Does anyone have any ideas
of what might cause this?

/ Alex

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Mon May 15 2000 - 21:00:21 EST