Messages in this thread Patch in this message | | | Date | Tue, 16 Sep 1997 15:24:50 -0400 | From | Bill Hawes <> | Subject | implementation of shrink_dcache_sb |
| |
I've attached a preliminary implementation of a shrink_dcache_sb() function to be used when unmounting a device or invalidating a device's inodes. This provides finer control over the dcache by not forcing a full shrink when we just need to remove a device.
To provide reasonable efficiency I move all leaf nodes to a temporary list, prune them from there, and then repeat as needed. Note that it doesn't matter if one of the dentries on the temporary list goes back into use while waiting to be freed; it will simply be unlinked from the list.
In testing so far this seems to work fine. I'm calling it from do_umount and from smbfs when invalidating inodes.
Comments or suggested improvements welcome. The patch is against pre-2.1.56.
Regards, Bill--- fs/dcache.c.old Sat Sep 13 21:36:53 1997 +++ fs/dcache.c Tue Sep 16 15:08:26 1997 @@ -119,13 +119,13 @@ * something (at which point we need to unuse * all dentries). */ -void prune_dcache(int count) +static void __prune_dcache(struct list_head *head, unsigned int count) { for (;;) { struct dentry *dentry; - struct list_head *tmp = dentry_unused.prev; + struct list_head *tmp = head->prev; - if (tmp == &dentry_unused) + if (tmp == head) break; list_del(tmp); INIT_LIST_HEAD(tmp); @@ -149,6 +149,53 @@ } } +void prune_dcache(int count) +{ + __prune_dcache(&dentry_unused, count); +} + +/* N.B. move to include/linux/dcache.h */ +extern void shrink_dcache_sb(struct super_block *); +/* + * Shrink the dcache for the specified super block. + * This allows us to unmount a device without disturbing + * the dcache for the other devices. + * + * We move the selected dentries to a temporary list, + * and then shrink that in the usual manner. + */ +void shrink_dcache_sb(struct super_block * sb) +{ + struct list_head *tmp, *prev; + struct dentry *dentry; + LIST_HEAD(temp); + +repeat: + prev = dentry_unused.prev; + while (prev != &dentry_unused) { + tmp = prev; + prev = tmp->prev; + dentry = list_entry(tmp, struct dentry, d_lru); + /* + * If the dentry is in use, we can always remove it. + * This makes less work for the next pass ... + */ + if (dentry->d_count) { + list_del(tmp); + INIT_LIST_HEAD(tmp); + continue; + } + if (dentry->d_sb == sb) { + list_del(tmp); + list_add(tmp, &temp); + } + } + if (!list_empty(&temp)) { + __prune_dcache(&temp, 0); + goto repeat; + } +} + #define NAME_ALLOC_LEN(len) ((len+16) & ~15) struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) | |