Updated patch for pre-patch 2.1.45-5

Bill Hawes (whawes@star.net)
Mon, 14 Jul 1997 21:42:33 -0400


This is a multi-part message in MIME format.
--------------7DF334A8349E0BC52F7E2E33
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

I've updated my patches for pre-patch 2.1.45-5, and everything is
working nicely. I got mount/umount/mount to work finally -- the problem
seems to be that in fs/super.c after the call to d_umount the root
dentry still has d_count=3. Since the call to d_delete won't let go of
the inode unless d_count == 1, I added two dputs. I'm sure this isn't
what you intended, but it works for now.

A few other changes -- I modified mark_inode_dirty to not do anything
unless s_op->write_inode is defined. Otherwise we get dirty pipe inodes
on the dirty list.

Also fixed an untested allocation in fs/super.c and an iput() for the
dummy inode.

I think you'll find the shift-scrolllock inode display helpful for
debugging.

Regards,
Bill
--------------7DF334A8349E0BC52F7E2E33
Content-Type: text/plain; charset=us-ascii; name="inode_pre45-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="inode_pre45-patch"

--- linux-2.1.45/fs/inode.c.old Mon Jul 14 15:25:23 1997
+++ linux-2.1.45/fs/inode.c Mon Jul 14 17:21:55 1997
@@ -18,6 +18,8 @@
* Famous last words.
*/

+#define INODE_PARANOIA 1
+
/*
* Inode lookup is no longer as critical as it used to be:
* most of the lookups are going to be through the dcache.
@@ -54,8 +56,9 @@
struct {
int nr_inodes;
int nr_free_inodes;
- int dummy[10];
-} inodes_stat;
+ int nr_may_free;
+ int dummy[9];
+} inodes_stat = {0, 0, 0};

int max_inodes = NR_INODE;

@@ -73,14 +76,14 @@
wake_up(&inode->i_wait);
}

-static void __wait_on_inode(struct inode * inode)
+static void __wait_on_inode_flag(struct inode * inode, int bit)
{
struct wait_queue wait = { current, NULL };

add_wait_queue(&inode->i_wait, &wait);
repeat:
current->state = TASK_UNINTERRUPTIBLE;
- if (test_bit(I_LOCK, &inode->i_state)) {
+ if (test_bit(bit, &inode->i_state)) {
schedule();
goto repeat;
}
@@ -88,6 +91,11 @@
current->state = TASK_RUNNING;
}

+static inline void __wait_on_inode(struct inode * inode)
+{
+ __wait_on_inode_flag(inode, I_LOCK);
+}
+
static inline void wait_on_inode(struct inode *inode)
{
if (test_bit(I_LOCK, &inode->i_state))
@@ -107,7 +115,6 @@
sema_init(&inode->i_sem, 1);
}

-
/*
* Look out! This returns with the inode lock held if
* it got an inode..
@@ -130,25 +137,42 @@
size -= sizeof(struct inode);
} while (size >= 0);
init_once(inode);
+ inodes_stat.nr_inodes += PAGE_SIZE / sizeof(struct inode);
}
return inode;
}

+/*
+ * This is called with I_LOCK set.
+ */
static inline void write_inode(struct inode *inode)
{
- if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode)
+ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode)
inode->i_sb->s_op->write_inode(inode);
}

-static inline void sync_list(struct list_head *head, struct list_head *clean)
+static void sync_list(struct list_head *head, struct list_head *clean,
+ kdev_t dev)
{
- struct list_head * tmp;
+ struct list_head * tmp, * sentinel = NULL;

- while ((tmp = head->prev) != head) {
+ while ((tmp = head->prev) != head && tmp != sentinel) {
struct inode *inode = list_entry(tmp, struct inode, i_list);
+
list_del(tmp);

/*
+ * If this inode isn't on the device we're syncing,
+ * move it to the front and set the sentinel.
+ */
+ if (dev && inode->i_dev != dev) {
+ list_add(tmp, head);
+ if (sentinel == NULL)
+ sentinel = tmp;
+ continue;
+ }
+
+ /*
* If the inode is locked, it's already being written out.
* We have to wait for it, though.
*/
@@ -164,6 +188,7 @@
write_inode(inode);
unlock_inode(inode);
}
+ sentinel = NULL;
spin_lock(&inode_lock);
}
}
@@ -176,28 +201,51 @@
void sync_inodes(kdev_t dev)
{
spin_lock(&inode_lock);
- sync_list(&inode_dirty, &inode_in_use);
+ sync_list(&inode_dirty, &inode_in_use, dev);
spin_unlock(&inode_lock);
}

/*
- * This is called by the filesystem to tell us
- * that the inode is no longer useful. We just
- * terminate it with extreme predjudice.
+ * Non-blocking. Caller must hold the inode lock, and must verify that
+ * the inode is not locked and that no disk quota exists. (Disk quota is
+ * released when the use count goes from 1 to 0.)
*/
-void clear_inode(struct inode *inode)
+static void _clear_inode(struct inode *inode)
{
+#ifdef INODE_PARANOIA
+ if (test_bit(I_LOCK, &inode->i_state))
+ printk("_clear_inode: inode %ld locked\n", inode->i_ino);
+ if (IS_WRITABLE(inode))
+ printk("_clear_inode: inode %ld disk quota not released\n",
+ inode->i_ino);
+#endif
+ /*
+ * This must be non-blocking to be completely safe.
+ */
truncate_inode_pages(inode, 0);
- wait_on_inode(inode);
- if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op)
- inode->i_sb->dq_op->drop(inode);

- spin_lock(&inode_lock);
inode->i_state = 0;
list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
list_del(&inode->i_list);
list_add(&inode->i_list, &inode_unused);
- spin_unlock(&inode_lock);
+}
+
+/*
+ * This is called by the filesystem to tell us
+ * that the inode is no longer useful. We just
+ * terminate it with extreme prejudice.
+ */
+void clear_inode(struct inode *inode)
+{
+#ifdef INODE_PARANOIA
+ if (atomic_read(&inode->i_count) != 1)
+ printk("clear_inode: invalid count %d\n",
+ atomic_read(&inode->i_count));
+ if (!test_bit(I_CLOSING, &inode->i_state))
+ printk("clear_inode: closing flag not set\n");
+#endif
+ set_bit(I_CLEARME, &inode->i_state);
}

#define CAN_UNUSE(inode) \
@@ -205,85 +253,146 @@
((inode)->i_nrpages == 0) && \
(!test_bit(I_LOCK, &(inode)->i_state)))

-static void invalidate_list(struct list_head *head, kdev_t dev)
+/*
+ * This is called with the inode lock held.
+ */
+static int invalidate_list(struct list_head *head, kdev_t dev, int check)
{
- struct list_head *next;
+ struct list_head *next, *tmp;
+ int busy = 0;

- next = head->next;
- for (;;) {
- struct list_head * tmp = next;
+ for (tmp = head->next; tmp != head; tmp = next) {
struct inode * inode;

- next = next->next;
- if (tmp == head)
- break;
+ next = tmp->next;
inode = list_entry(tmp, struct inode, i_list);
if (inode->i_dev != dev)
continue;
- if (!CAN_UNUSE(inode))
- continue;
- list_del(&inode->i_hash);
- list_del(&inode->i_list);
- list_add(&inode->i_list, &inode_unused);
+ if (atomic_read(&(inode)->i_count) == 0 &&
+ !test_bit(I_DIRTY, &inode->i_state) &&
+ !test_bit(I_LOCK , &inode->i_state))
+ _clear_inode(inode);
+ else {
+ busy = 1;
+ if (check) {
+#ifdef INODE_PARANOIA
+printk("invalidate_inodes: device %d inode %ld count=%d state=%x\n",
+dev, inode->i_ino, atomic_read(&inode->i_count), inode->i_state);
+#endif
+ break;
+ }
+ }
}
+ return busy;
}

void invalidate_inodes(kdev_t dev)
{
spin_lock(&inode_lock);
- invalidate_list(&inode_in_use, dev);
- invalidate_list(&inode_dirty, dev);
+ invalidate_list(&inode_in_use, dev, 0);
+ invalidate_list(&inode_dirty , dev, 0);
spin_unlock(&inode_lock);
}

/*
- * This is called with the inode lock held. It just looks at the last
- * inode on the in-use list, and if the inode is trivially freeable
- * we just move it to the unused list.
+ * This is called with the inode lock held. It checks for potentially
+ * freeable inodes and uses _clear_inode to free them.
*
- * Otherwise we just move the inode to be the first inode and expect to
- * get back to the problem later..
+ * If the first pass fails, we try syncing and check again.
*/
static void try_to_free_inodes(void)
{
- struct list_head * tmp;
+ struct list_head *tmp, *prev;
struct list_head *head = &inode_in_use;
+ int passes = 2, goal = 10, freed = 0;

- tmp = head->prev;
- if (tmp != head) {
- struct inode * inode;
+ /*
+ * nr_may_free is an (over)estimate of the number of unused inodes,
+ * based on zero use counts as seen by iput(). This is useful as
+ * a futility avoidance measure.
+ */
+ if (!inodes_stat.nr_may_free)
+ return;
+ /*
+ * Adjust our goal if there aren't very many inodes.
+ */
+ if (goal > inodes_stat.nr_may_free)
+ goal = inodes_stat.nr_may_free;
+ if (goal > (inodes_stat.nr_inodes >> 5))
+ goal = inodes_stat.nr_inodes >> 5;
+
+ while (passes--) {
+ for (tmp = head->prev; tmp != head; tmp = prev) {
+ struct inode * inode;
+
+ prev = tmp->prev;
+ inode = list_entry(tmp, struct inode, i_list);
+ if (atomic_read(&(inode)->i_count) != 0)
+ continue;
+ if (test_bit(I_LOCK, &(inode)->i_state))
+ continue;
+ /*
+ * Inode is potentially freeable ... should select
+ * least cost inodes though.
+ */
+ _clear_inode(inode);
+ inodes_stat.nr_may_free--;
+ freed++;
+ if (freed >= goal)
+ return;
+ }

- list_del(tmp);
- inode = list_entry(tmp, struct inode, i_list);
- if (CAN_UNUSE(inode)) {
- list_del(&inode->i_hash);
- head = &inode_unused;
+ if (freed)
+ return;
+
+ /*
+ * First pass failed ... maybe syncing will help.
+ */
+ if (inode_dirty.next != &inode_dirty) {
+ sync_list(&inode_dirty, &inode_in_use, 0);
+ } else {
+ /*
+ * None available ... reset the "maybe free" counter.
+ */
+ inodes_stat.nr_may_free = 0;
+ break;
}
- list_add(tmp, head);
}
}
-

+/*
+ * Called with the inode lock held.
+ */
static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head)
{
struct list_head *tmp;
struct inode * inode;

- tmp = head;
- for (;;) {
- tmp = tmp->next;
- inode = NULL;
- if (tmp == head)
- break;
+repeat:
+ for (tmp = head->next; tmp != head; tmp = tmp->next) {
inode = list_entry(tmp, struct inode, i_hash);
if (inode->i_sb != sb)
continue;
if (inode->i_ino != ino)
continue;
+ if (test_bit(I_CLOSING, &inode->i_state))
+ goto wait_for_close;
atomic_inc(&inode->i_count);
- break;
+ return inode;
}
- return inode;
+ return NULL;
+
+wait_for_close:
+ /*
+ * Inode is being taken out of service ... wait and check again.
+ */
+ spin_unlock(&inode_lock);
+#ifdef INODE_PARANOIA
+printk("find_inode: inode %ld dev %d closing\n", inode->i_ino, inode->i_dev);
+#endif
+ __wait_on_inode_flag(inode, I_CLOSING);
+ spin_lock(&inode_lock);
+ goto repeat;
}

/*
@@ -293,11 +402,12 @@
* i_sb, i_ino, i_count, i_state and the lists have
* been initialized elsewhere..
*/
-void clean_inode(struct inode *inode)
+static void clean_inode(struct inode *inode)
{
memset(&inode->u, 0, sizeof(inode->u));
inode->i_pipe = 0;
inode->i_sock = 0;
+ inode->i_mode = 0;
inode->i_op = NULL;
inode->i_nlink = 1;
inode->i_writecount = 0;
@@ -321,62 +431,81 @@
struct inode * inode;
struct list_head * tmp = inode_unused.next;

+ spin_lock(&inode_lock);
if (tmp != &inode_unused) {
+get_unused:
list_del(tmp);
inode = list_entry(tmp, struct inode, i_list);
+#ifdef INODE_PARANOIA
+ if (atomic_read(&inode->i_count))
+ printk("get_empty_inode: unused count=%d\n",
+ atomic_read(&inode->i_count));
+#endif
+
add_new_inode:
INIT_LIST_HEAD(&inode->i_list);
INIT_LIST_HEAD(&inode->i_hash);
inode->i_sb = NULL;
+ inode->i_dev = 0;
inode->i_ino = ++last_ino;
- atomic_set(&inode->i_count, 1);
+ inode->i_flags = 0;
+ atomic_inc(&inode->i_count);
inode->i_state = 0;
+ spin_unlock(&inode_lock);
clean_inode(inode);
return inode;
}

/*
- * Warning: if this succeeded, we will now
- * return with the inode lock, and we need to
- * unlock it.
+ * No unused inodes ... try to free some.
*/
+ try_to_free_inodes();
+ tmp = inode_unused.next;
+ if (tmp != &inode_unused)
+ goto get_unused;
+
+ /*
+ * Unlock to try to allocate more inodes.
+ */
+ spin_unlock(&inode_lock);
inode = grow_inodes();
- if (inode) {
- spin_unlock(&inode_lock);
+ if (inode)
goto add_new_inode;
- }
return inode;
}

struct inode * get_pipe_inode(void)
{
extern struct inode_operations pipe_inode_operations;
- struct inode *inode = get_empty_inode();
+ struct inode *inode;
+ unsigned long page = __get_free_page(GFP_USER);

- if (inode) {
- unsigned long page = __get_free_page(GFP_USER);
+ if (!page)
+ goto fail;

- if (!page) {
- iput(inode);
- inode = NULL;
- } else {
- PIPE_BASE(*inode) = (char *) page;
- inode->i_op = &pipe_inode_operations;
- atomic_set(&inode->i_count, 1);
- PIPE_WAIT(*inode) = NULL;
- PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
- PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
- PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
- PIPE_LOCK(*inode) = 0;
- inode->i_pipe = 1;
- inode->i_mode |= S_IFIFO | S_IRUSR | S_IWUSR;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_blksize = PAGE_SIZE;
- }
- }
+ inode = get_empty_inode();
+ if (!inode)
+ goto no_inode;
+
+ PIPE_BASE(*inode) = (char *) page;
+ inode->i_op = &pipe_inode_operations;
+ PIPE_WAIT(*inode) = NULL;
+ PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
+ PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
+ PIPE_LOCK(*inode) = 0;
+ inode->i_pipe = 1;
+ inode->i_mode |= S_IFIFO | S_IRUSR | S_IWUSR;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blksize = PAGE_SIZE;
return inode;
+
+no_inode:
+ free_page(page);
+fail:
+ return NULL;
}

/*
@@ -388,8 +517,14 @@
struct list_head * tmp = inode_unused.next;

if (tmp != &inode_unused) {
+get_unused:
list_del(tmp);
inode = list_entry(tmp, struct inode, i_list);
+#ifdef INODE_PARANOIA
+ if (atomic_read(&(inode)->i_count))
+ printk("get_new_inode: unused count=%d\n", atomic_read(&(inode)->i_count));
+#endif
+
add_new_inode:
list_add(&inode->i_list, &inode_in_use);
list_add(&inode->i_hash, head);
@@ -397,7 +532,7 @@
inode->i_dev = sb->s_dev;
inode->i_ino = ino;
inode->i_flags = sb->s_flags;
- atomic_set(&inode->i_count, 1);
+ atomic_inc(&inode->i_count);
inode->i_state = 1 << I_LOCK;
spin_unlock(&inode_lock);
clean_inode(inode);
@@ -406,6 +541,14 @@
}

/*
+ * No unused inodes ... try to free some.
+ */
+ try_to_free_inodes();
+ tmp = inode_unused.next;
+ if (tmp != &inode_unused)
+ goto get_unused;
+
+ /*
* Uhhuh.. We need to expand. Unlock for the allocation,
* but note that "grow_inodes()" will return with the
* lock held again if the allocation succeeded.
@@ -440,7 +583,6 @@
spin_lock(&inode_lock);
inode = find_inode(sb, ino, head);
if (!inode) {
- try_to_free_inodes();
return get_new_inode(sb, ino, head);
}
spin_unlock(&inode_lock);
@@ -451,28 +593,84 @@
void insert_inode_hash(struct inode *inode)
{
struct list_head *head = inode_hashtable + hash(inode->i_sb, inode->i_ino);
+ spin_lock(&inode_lock);
list_add(&inode->i_hash, head);
+ spin_unlock(&inode_lock);
}

void iput(struct inode *inode)
{
- if (inode) {
- if (inode->i_pipe)
- wake_up_interruptible(&PIPE_WAIT(*inode));
+ if (!inode)
+ return;
+
+ if (inode->i_pipe)
+ wake_up_interruptible(&PIPE_WAIT(*inode));

+ /*
+ * Last user dropping the inode?
+ */
+ if (atomic_read(&inode->i_count) == 1) {
/*
- * Last user dropping the inode?
- */
- if (atomic_read(&inode->i_count) == 1) {
+ * Set the I_CLOSING bit so the inode can't be put back
+ * in use while we're taking it out of service.
+ */
+ set_bit(I_CLOSING, &inode->i_state);
+
+ wait_on_inode(inode);
+ if (test_and_clear_bit(I_DIRTY, &inode->i_state)) {
+ set_bit(I_LOCK, &inode->i_state);
+ write_inode(inode);
+ unlock_inode(inode);
+ }
+ if (inode->i_sb && inode->i_sb->s_op) {
void (*put)(struct inode *);
- if (inode->i_sb && inode->i_sb->s_op) {
- put = inode->i_sb->s_op->put_inode;
- if (put)
- put(inode);
+ put = inode->i_sb->s_op->put_inode;
+ if (put)
+ put(inode);
+ }
+ /*
+ * Pipe inodes have to be handled specially ...
+ */
+ if (inode->i_pipe) {
+ free_page((unsigned long)PIPE_BASE(*inode));
+ PIPE_BASE(*inode) = NULL;
+ clear_inode(inode);
+ }
+
+ /*
+ * Check whether the filesystem asked us to clear the inode.
+ */
+ if (test_and_clear_bit(I_CLEARME, &inode->i_state)) {
+ wait_on_inode(inode);
+ /*
+ * Drop the disk quota when the use count goes to 0.
+ */
+ if (IS_WRITABLE(inode) && inode->i_sb &&
+ inode->i_sb->dq_op) {
+ set_bit(I_LOCK, &inode->i_state);
+ inode->i_sb->dq_op->drop(inode);
+ unlock_inode(inode);
}
+
+ spin_lock(&inode_lock);
+ _clear_inode(inode);
+ spin_unlock(&inode_lock);
}
- atomic_dec(&inode->i_count);
+ /*
+ * All safe now ... clear the "closing" flag.
+ */
+ clear_bit(I_CLOSING, &inode->i_state);
+ wake_up(&inode->i_wait);
}
+ atomic_dec(&inode->i_count);
+ if (!atomic_read(&inode->i_count))
+ inodes_stat.nr_may_free++;
+
+#ifdef INODE_PARANOIA
+if (atomic_read(&inode->i_count) < 0)
+printk("iput: open count wrapped dev=%d inode=%ld count=%d\n",
+inode->i_dev, inode->i_ino, atomic_read(&inode->i_count));
+#endif
}

int bmap(struct inode * inode, int block)
@@ -499,20 +697,143 @@
}

/*
- * FIXME! These need to go through the in-use inodes to
- * check whether we can mount/umount/remount.
+ * Check whether we can mount.
*/
int fs_may_mount(kdev_t dev)
{
- return 1;
+ int busy;
+
+ /*
+ * Shrink the dcache to free all unused dentries ...
+ */
+ shrink_dcache();
+
+ spin_lock(&inode_lock);
+ busy = invalidate_list(&inode_in_use, dev, 1) ||
+ invalidate_list(&inode_dirty , dev, 1);
+ spin_unlock(&inode_lock);
+#ifdef INODE_PARANOIA
+ printk("fs_may_mount: device %d busy=%d\n", dev, busy);
+#endif
+ return !busy;
}

+/*
+ * Check whether we can unmount.
+ */
int fs_may_umount(kdev_t dev, struct dentry * root)
{
- return 0;
+ struct list_head * tmp;
+ struct list_head *head = &inode_in_use;
+ int busy = 0;
+
+ /*
+ * Shrink the dcache to free all unused dentries ...
+ */
+ shrink_dcache();
+#ifdef INODE_PARANOIA
+ printk("fs_may_umount: after shrink d_count=%d\n", root->d_count);
+#endif
+ /*
+ * ... then sync this device and invalidate the inodes.
+ */
+ spin_lock(&inode_lock);
+ sync_list(&inode_dirty, &inode_in_use, dev);
+ invalidate_list(&inode_in_use, dev, 0);
+
+ /*
+ * Check to see if anything is still in use ...
+ */
+ for (tmp = head->next; tmp != head; tmp = tmp->next) {
+ struct inode * inode;
+
+ inode = list_entry(tmp, struct inode, i_list);
+ if (inode->i_dev != dev)
+ continue;
+ if (!atomic_read(&inode->i_count))
+ continue;
+ if (inode != root->d_inode) {
+#ifdef INODE_PARANOIA
+ printk("fs_may_umount: inode %ld in use, not root inode %ld\n",
+ inode->i_ino, root->d_inode->i_ino);
+#endif
+ busy = 1;
+ break;
+ }
+ }
+ spin_unlock(&inode_lock);
+ return !busy;
}

+/*
+ * Check that no files on this device are currently open for writing.
+ */
int fs_may_remount_ro(kdev_t dev)
{
- return 0;
+ struct file * file;
+ struct inode * inode;
+
+ for (file = inuse_filps; file; file = file->f_next) {
+ if (!(inode = file->f_dentry->d_inode))
+ continue;
+ if (inode->i_dev != dev)
+ continue;
+ if (S_ISREG(inode->i_mode) && (file->f_mode & 2))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Debugging aids
+ */
+void show_inodes(void)
+{
+ struct list_head *head;
+ int nlist, accounted = 0, tot_pages = 0;
+ static char *list_types[3] = {"CLEAN ","DIRTY ", "UNUSED"};
+
+ printk("Inodes Allocated: %d\n", inodes_stat.nr_inodes);
+ printk("List Inodes In-Use Hashed Locked Dirty Pipe Cnt<0"
+ " Pages\n");
+ for (nlist = 0, head = &inode_in_use; nlist < 3; nlist++, head++) {
+ struct list_head *tmp;
+ struct inode *inode;
+ int found, used, pages, hashed, locked, dirty , pipe, negative;
+ int count;
+
+ found = used = pages = hashed = locked = dirty = pipe = 0;
+ negative = 0;
+ for (tmp = head->next; tmp != head; tmp = tmp->next) {
+ found++;
+
+ inode = list_entry(tmp, struct inode, i_list);
+ if ((count = atomic_read(&inode->i_count)) > 0)
+ used++;
+ if (inode->i_hash.next &&
+ inode->i_hash.next != &inode->i_hash)
+ hashed++;
+ if (test_bit(I_LOCK, &inode->i_state))
+ locked++;
+ if (test_bit(I_DIRTY, &inode->i_state))
+ dirty++;
+ /*
+ * Check for problems ... these shouldn't happen.
+ */
+ if (inode->i_pipe)
+ pipe++;
+ if (count < 0)
+ negative++;
+ /*
+ * Count the number of cache pages.
+ */
+ pages += inode->i_nrpages;
+ }
+ printk("%6s %6d %6d %6d %6d %5d %4d %5d %6d\n",
+ list_types[nlist], found, used, hashed, locked, dirty,
+ pipe, negative, pages);
+ accounted += found;
+ tot_pages += pages;
+ }
+ printk("Inodes Accounted: %d Total Pages: %d\n", accounted, tot_pages);
}
--- linux-2.1.45/include/linux/fs.h.old Mon Jul 14 15:41:27 1997
+++ linux-2.1.45/include/linux/fs.h Mon Jul 14 20:59:44 1997
@@ -358,13 +358,8 @@
/* Inode state bits.. */
#define I_DIRTY 0
#define I_LOCK 1
-
-extern void __mark_inode_dirty(struct inode *);
-static inline void mark_inode_dirty(struct inode *inode)
-{
- if (!test_and_set_bit(I_DIRTY, &inode->i_state))
- __mark_inode_dirty(inode);
-}
+#define I_CLOSING 2 /* Inode count going from 1 -> 0 */
+#define I_CLEARME 3 /* Filesystem asked to clear inode */

struct file {
struct file *f_next, **f_pprev;
@@ -591,6 +586,20 @@
struct file_system_type * next;
};

+extern void __mark_inode_dirty(struct inode *);
+/*
+ * Mark the inode dirty _only_ if it implements s_op->write_inode.
+ * (We want to keep pipe inodes out of the dirty list.)
+ */
+static inline void mark_inode_dirty(struct inode *inode)
+{
+ if (inode->i_sb &&
+ inode->i_sb->s_op &&
+ inode->i_sb->s_op->write_inode &&
+ !test_and_set_bit(I_DIRTY, &inode->i_state))
+ __mark_inode_dirty(inode);
+}
+
extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *);

@@ -787,6 +796,7 @@
extern kdev_t ROOT_DEV;

extern void show_buffers(void);
+extern void show_inode(void);
extern void mount_root(void);

#ifdef CONFIG_BLK_DEV_INITRD
--- linux-2.1.45/include/linux/list.h.old Mon Jul 14 15:25:25 1997
+++ linux-2.1.45/include/linux/list.h Mon Jul 14 16:04:08 1997
@@ -27,10 +27,19 @@
static inline void list_del(struct list_head *entry)
{
struct list_head *next, *prev;
+#ifdef INODE_PARANOIA
+if (!entry->next) {
+printk("list_del: already removed! (file %s line %s)\n", __FILE__, __LINE__);
+return; }
+#endif
next = entry->next;
prev = entry->prev;
next->prev = prev;
prev->next = next;
+#ifdef INODE_PARANOIA
+entry->next = NULL;
+entry->prev = NULL;
+#endif
}

static inline int list_empty(struct list_head *head)
--- linux-2.1.45/fs/super.c.old Mon Jul 14 15:25:24 1997
+++ linux-2.1.45/fs/super.c Mon Jul 14 20:39:32 1997
@@ -466,7 +466,7 @@
}
if (!(sb = get_super(dev)))
return;
- if (sb->s_root != sb->s_root->d_mounts) {
+ if (sb->s_root && sb->s_root != sb->s_root->d_mounts) {
printk("VFS: Mounted device %s - tssk, tssk\n",
kdevname(dev));
return;
@@ -588,6 +588,7 @@
{
if (covers->d_mounts != covers) {
printk("VFS: mount - already mounted\n");
+ /* WSH: shouldn't we dput(covers) here?? */
return;
}
covers->d_mounts = dget(dentry);
@@ -597,15 +598,18 @@
static int do_umount(kdev_t dev,int unmount_root)
{
struct super_block * sb;
+ struct dentry * root;
int retval;

+ sb=get_super(dev);
+ if (!sb)
+ return -ENOENT;
+
if (dev==ROOT_DEV && !unmount_root) {
/*
* Special case for "unmounting" root. We just try to remount
* it readonly, and sync() the device.
*/
- if (!(sb=get_super(dev)))
- return -ENOENT;
if (!(sb->s_flags & MS_RDONLY)) {
/*
* Make sure all quotas are turned off on this device we need to mount
@@ -621,9 +625,6 @@
}
return 0;
}
- sb=get_super(dev);
- if (!sb)
- return -ENOENT;

/*
* Before checking if the filesystem is still busy make sure the kernel
@@ -631,16 +632,23 @@
* too bad there are no quotas running anymore. Turn them on again by hand.
*/
quota_off(dev, -1);
- if (!fs_may_umount(dev, sb->s_root))
+ root = sb->s_root;
+ if (!fs_may_umount(dev, root))
return -EBUSY;

/* Clear up the dcache tree. This should be cleaner.. */
- if (sb->s_root) {
- d_umount(sb->s_root);
- d_delete(sb->s_root);
+ if (root) {
+if (root->d_inode)
+printk("do_umount: before d_umount d_count=%d, inode=%ld, i_count=%d\n",
+root->d_count, root->d_inode->i_ino, root->d_inode->i_count);
+ d_umount(root);
+ /* WSH: ugly hack to get root dentry to let go of inode */
+ dput(root);
+ dput(root);
+ d_delete(root);
+ sb->s_root = NULL;
}

- sb->s_root = NULL;
if (sb->s_op && sb->s_op->write_super && sb->s_dirt)
sb->s_op->write_super(sb);
put_super(dev);
@@ -681,24 +689,26 @@
dev = inode->i_rdev;
retval = -EACCES;
if (IS_NODEV(inode)) {
- dput(dentry);
- goto out;
+ goto dput_out;
}
} else {
retval = -EINVAL;
if (!inode->i_sb || inode != inode->i_sb->s_root->d_inode) {
- dput(dentry);
- goto out;
+ goto dput_out;
}
dev = inode->i_sb->s_dev;
- dput(dentry);
+ /* WSH: This dentry is dput later ... */
+ /* dput(dentry); */
inode = dummy_inode = get_empty_inode();
+ if (!dummy_inode) {
+ retval = -ENOMEM;
+ goto dput_out;
+ }
inode->i_rdev = dev;
}
- retval = -ENXIO;
if (MAJOR(dev) >= MAX_BLKDEV) {
- dput(dentry);
- goto out;
+ retval = -ENXIO;
+ goto dput_out;
}
retval = do_umount(dev,0);
if (!retval) {
@@ -711,8 +721,13 @@
dput(dentry);
if (!retval)
fsync_dev(dev);
+ goto out;
+
+dput_out:
+ dput(dentry);
out:
unlock_kernel();
+ iput(dummy_inode);
return retval;
}

@@ -1041,6 +1056,7 @@
#endif

memset(&filp, 0, sizeof(filp));
+ /* WSH: unchecked allocation */
d_inode = get_empty_inode();
d_inode->i_rdev = ROOT_DEV;
filp.f_dentry = NULL;
--- linux-2.1.45/fs/namei.c.old Mon Jul 14 15:25:24 1997
+++ linux-2.1.45/fs/namei.c Mon Jul 14 16:04:08 1997
@@ -183,11 +183,19 @@
* We use "fsuid" for this, letting us set arbitrary permissions
* for filesystem access without changing the "normal" uids which
* are used for other things..
+ *
+ * WSH: Check for NULL inode (possibly a zombie in path?)
*/
int permission(struct inode * inode,int mask)
{
- int mode = inode->i_mode;
+ int mode;

+ if (!inode) {
+printk("permission: null inode\n");
+ return -EACCES; /* deny access */
+ }
+
+ mode = inode->i_mode;
if (inode->i_op && inode->i_op->permission)
return inode->i_op->permission(inode, mask);
else if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
--- linux-2.1.45/fs/ext2/ialloc.c.old Mon Jul 14 15:25:23 1997
+++ linux-2.1.45/fs/ext2/ialloc.c Mon Jul 14 16:04:08 1997
@@ -404,7 +404,6 @@
sb->s_dirt = 1;
inode->i_mode = mode;
inode->i_sb = sb;
- atomic_set(&inode->i_count, 1);
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
--- linux-2.1.45/arch/i386/mm/init.c.old Mon May 12 13:35:38 1997
+++ linux-2.1.45/arch/i386/mm/init.c Mon Jul 14 16:04:08 1997
@@ -31,6 +31,7 @@

extern void die_if_kernel(char *,struct pt_regs *,long);
extern void show_net_buffers(void);
+extern void show_inodes(void);

/*
* BAD_PAGE is the page that is used for page faults when linux
@@ -92,6 +93,7 @@
printk("%d reserved pages\n",reserved);
printk("%d pages shared\n",shared);
show_buffers();
+ show_inodes();
#ifdef CONFIG_NET
show_net_buffers();
#endif
--- linux-2.1.45/fs/sysv/ialloc.c.old Thu Jun 26 14:20:54 1997
+++ linux-2.1.45/fs/sysv/ialloc.c Mon Jul 14 16:04:09 1997
@@ -150,23 +150,22 @@
mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
sb->s_dirt = 1; /* and needs time stamp */
- atomic_set(&inode->i_count, 1);
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
- inode->i_dirt = 1;
inode->i_ino = ino;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_op = NULL;
inode->i_blocks = inode->i_blksize = 0;
+ mark_inode_dirty(inode);
insert_inode_hash(inode);
/* Change directory entry: */
inode->i_mode = 0; /* for sysv_write_inode() */
inode->i_size = 0; /* ditto */
sysv_write_inode(inode); /* ensure inode not allocated again */
/* FIXME: caller may call this too. */
- inode->i_dirt = 1; /* cleared by sysv_write_inode() */
+ mark_inode_dirty(inode); /* cleared by sysv_write_inode() */
/* That's it. */
(*sb->sv_sb_total_free_inodes)--;
mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */
--- linux-2.1.45/fs/affs/inode.c.old Thu Jun 26 14:20:50 1997
+++ linux-2.1.45/fs/affs/inode.c Mon Jul 14 16:04:09 1997
@@ -899,12 +899,10 @@
return NULL;
}

- atomic_set(&inode->i_count, 1);
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
- inode->i_dirt = 1;
inode->i_ino = block;
inode->i_op = NULL;
inode->i_blocks = 0;
@@ -924,6 +922,7 @@
inode->u.affs_i.i_cache_users = 0;
inode->u.affs_i.i_lastblock = -1;

+ mark_inode_dirty(inode);
insert_inode_hash(inode);

return inode;
--- linux-2.1.45/fs/minix/bitmap.c.old Thu Jun 26 14:20:52 1997
+++ linux-2.1.45/fs/minix/bitmap.c Mon Jul 14 16:04:09 1997
@@ -251,16 +251,15 @@
iput(inode);
return NULL;
}
- atomic_set(&inode->i_count, 1);
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
- inode->i_dirt = 1;
inode->i_ino = j;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_op = NULL;
inode->i_blocks = inode->i_blksize = 0;
+ mark_inode_dirty(inode);
insert_inode_hash(inode);
return inode;
}

--------------7DF334A8349E0BC52F7E2E33--