[patch] sched, vfs: fix scheduling latencies in invalidate_inodes()

Ingo Molnar
2004-09-14T10:12:16Z

[-- Attachment #1: Type: text/plain, Size: 315 bytes --]

the attached patch fixes long scheduling latencies in
invalidate_inodes(). The lock-break is a bit tricky to not get into a
livelock scenario: we use a dummy inode as a marker at which point we
can continue the scanning after the schedule.

this patch has been tested as part of the -VP patchset for weeks.

Ingo

[-- Attachment #2: fix-latency-invalidate-inodes.patch --]
[-- Type: text/plain, Size: 2629 bytes --]

the attached patch fixes long scheduling latencies in
invalidate_inodes(). The lock-break is a bit tricky to not get into a
livelock scenario: we use a dummy inode as a marker at which point we
can continue the scanning after the schedule.

this patch has been tested as part of the -VP patchset for weeks.

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- linux/fs/inode.c.orig
+++ linux/fs/inode.c
@@ -296,7 +296,7 @@ static void dispose_list(struct list_hea
/*
* Invalidate all inodes for a device.
*/
-static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose)
+static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose, struct list_head *mark)
{
struct list_head *next;
int busy = 0, count = 0;
@@ -306,6 +306,20 @@ static int invalidate_list(struct list_h
struct list_head * tmp = next;
struct inode * inode;

+ /*
+ * Preempt if necessary. To make this safe we use a dummy
+ * inode as a marker - we can continue off that point.
+ * We rely on this sb's inodes (including the marker) not
+ * getting reordered within the list during umount. Other
+ * inodes might get reordered.
+ */
+ if (lock_need_resched(&inode_lock)) {
+ list_add_tail(mark, next);
+ BUG_ON(mark->next != next);
+ cond_resched_lock(&inode_lock);
+ tmp = next = mark->next;
+ list_del(mark);
+ }
next = next->next;
if (tmp == head)
break;
@@ -347,18 +361,26 @@ int invalidate_inodes(struct super_block
{
int busy;
LIST_HEAD(throw_away);
+ struct inode *marker;
+ struct list_head *mark;
+
+ marker = kmalloc(sizeof(*marker), SLAB_KERNEL|__GFP_REPEAT);
+ memset(marker, 0, sizeof(*marker));
+ mark = &marker->i_list;

down(&iprune_sem);
spin_lock(&inode_lock);
- busy = invalidate_list(&inode_in_use, sb, &throw_away);
- busy |= invalidate_list(&inode_unused, sb, &throw_away);
- busy |= invalidate_list(&sb->s_dirty, sb, &throw_away);
- busy |= invalidate_list(&sb->s_io, sb, &throw_away);
+ busy = invalidate_list(&inode_in_use, sb, &throw_away, mark);
+ busy |= invalidate_list(&inode_unused, sb, &throw_away, mark);
+ busy |= invalidate_list(&sb->s_dirty, sb, &throw_away, mark);
+ busy |= invalidate_list(&sb->s_io, sb, &throw_away, mark);
spin_unlock(&inode_lock);

dispose_list(&throw_away);
up(&iprune_sem);

+ kfree(marker);
+
return busy;
}

@@ -429,6 +451,8 @@ static void prune_icache(int nr_to_scan)
for (nr_scanned = 0; nr_scanned < nr_to_scan; nr_scanned++) {
struct inode *inode;

+ cond_resched_lock(&inode_lock);
+
if (list_empty(&inode_unused))
break;