[patch] inode leakage again

Andrea Arcangeli (andrea@e-mind.com)
Fri, 5 Feb 1999 01:59:17 +0100 (CET)


Oh well, I understood the way the old code was working putting stuff from
the back of the list to the top of the list in order to get a nice round
robin browsing of the inuse list.

Oleg can you confirm me that this my new patch against 2.2.1 (or
2.2.2-pre1) fix everything for you? I can't produce lockups or leakage
anymore here.

I also fixed the limit of the inodes to not grow over inode-max otherwise
a malicious attacker could simply launch many proggy that only open in
loop at the same time many inodes and could cause the inode pool to grow
up too much (far over inode-max), and since it's not shrinkable the admin
would have to reset to shrink the inode list ;).

Index: inode.c
===================================================================
RCS file: /var/cvs/linux/fs/inode.c,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 inode.c
--- inode.c 1999/01/26 18:30:24 1.1.1.2
+++ linux/fs/inode.c 1999/02/05 00:31:58
@@ -62,9 +62,8 @@
struct {
int nr_inodes;
int nr_free_inodes;
- int preshrink; /* pre-shrink dcache? */
- int dummy[4];
-} inodes_stat = {0, 0, 0,};
+ int dummy[5];
+} inodes_stat = {0, 0, };

int max_inodes;

@@ -329,11 +328,12 @@

static int free_inodes(int goal)
{
- struct list_head *tmp, *head = &inode_in_use;
+ struct list_head *tmp, *head = &inode_in_use, *first;
LIST_HEAD(freeable);
- int found = 0, depth = goal << 1;
+ int found = 0;

- while ((tmp = head->prev) != head && depth--) {
+ first = head->next;
+ while ((tmp = head->prev) != head && tmp != first) {
struct inode * inode = list_entry(tmp, struct inode, i_list);
list_del(tmp);
if (CAN_UNUSE(inode)) {
@@ -403,7 +403,7 @@
/*
* Check whether to restock the unused list.
*/
- if (inodes_stat.preshrink) {
+ if (inodes_stat.nr_inodes > max_inodes) {
struct list_head *tmp;
try_to_free_inodes(8);
tmp = inode_unused.next;
@@ -413,6 +413,9 @@
inode = list_entry(tmp, struct inode, i_list);
return inode;
}
+ spin_unlock(&inode_lock);
+ printk(KERN_WARNING "grow_inodes: inode-max limit reached\n");
+ return NULL;
}

spin_unlock(&inode_lock);
@@ -436,9 +439,6 @@
*/
inodes_stat.nr_inodes += INODES_PER_PAGE;
inodes_stat.nr_free_inodes += INODES_PER_PAGE - 1;
- inodes_stat.preshrink = 0;
- if (inodes_stat.nr_inodes > max_inodes)
- inodes_stat.preshrink = 1;
return inode;
}

@@ -447,7 +447,6 @@
* the dcache and then try again to free some inodes.
*/
prune_dcache(inodes_stat.nr_inodes >> 2);
- inodes_stat.preshrink = 1;

spin_lock(&inode_lock);
free_inodes(inodes_stat.nr_inodes >> 2);
@@ -462,7 +461,7 @@
}
spin_unlock(&inode_lock);

- printk("grow_inodes: allocation failed\n");
+ printk(KERN_WARNING "grow_inodes: allocation failed\n");
return NULL;
}

_Don't_ be happy (maybe this time Murphy will hear my words? ;)

Now there's a secondary major problem that I am going to fix now. The
problem is that inodes seems to stay busy even if they are unused from
your proggy so you'll get many `inode-max limit reached' messages (but
the system remains perfectly stable here and you can increase inode-max
to workaround temporarly the problem).

I think this is caused by a bug somewhere but it's completly unrelated to
the fix above.

Andrea Arcangeli

-
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/