diff options
author | Andrew Morton <akpm@osdl.org> | 2005-10-23 15:57:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-10-23 19:38:39 -0400 |
commit | 8d3b35914aa54232b27e6a2b57d84092aadc5e86 (patch) | |
tree | 809be8fad6743a751964677e68c5c01af3198e4b | |
parent | c0fef676bbd75e711711ed3ff5bebb7bfd1bdb00 (diff) |
[PATCH] inotify/idr leak fix
Fix a bug which was reported and diagnosed by
Stefan Jones <stefan.jones@churchillrandoms.co.uk>
IDR trees include a cache of idr_layer objects. There's no way to destroy
this cache, so when we discard an overall idr tree we end up leaking some
memory.
Add and use idr_destroy() for this. v9fs and infiniband also need to use
idr_destroy() to avoid leaks.
Or, we make the cache global, like radix_tree_preload(). Which is probably
better. Later.
Cc: Eric Van Hensbergen <ericvh@ericvh.myip.org>
Cc: Roland Dreier <rolandd@cisco.com>
Cc: Robert Love <rml@novell.com>
Cc: John McCutchan <ttb@tentacle.dhs.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/inotify.c | 1 | ||||
-rw-r--r-- | include/linux/idr.h | 1 | ||||
-rw-r--r-- | lib/idr.c | 13 |
3 files changed, 15 insertions, 0 deletions
diff --git a/fs/inotify.c b/fs/inotify.c index a37e9fb1da58..9fbaebfdf40b 100644 --- a/fs/inotify.c +++ b/fs/inotify.c | |||
@@ -176,6 +176,7 @@ static inline void put_inotify_dev(struct inotify_device *dev) | |||
176 | if (atomic_dec_and_test(&dev->count)) { | 176 | if (atomic_dec_and_test(&dev->count)) { |
177 | atomic_dec(&dev->user->inotify_devs); | 177 | atomic_dec(&dev->user->inotify_devs); |
178 | free_uid(dev->user); | 178 | free_uid(dev->user); |
179 | idr_destroy(&dev->idr); | ||
179 | kfree(dev); | 180 | kfree(dev); |
180 | } | 181 | } |
181 | } | 182 | } |
diff --git a/include/linux/idr.h b/include/linux/idr.h index ca3b7e462576..3d5de45f961b 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h | |||
@@ -75,4 +75,5 @@ int idr_pre_get(struct idr *idp, unsigned gfp_mask); | |||
75 | int idr_get_new(struct idr *idp, void *ptr, int *id); | 75 | int idr_get_new(struct idr *idp, void *ptr, int *id); |
76 | int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id); | 76 | int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id); |
77 | void idr_remove(struct idr *idp, int id); | 77 | void idr_remove(struct idr *idp, int id); |
78 | void idr_destroy(struct idr *idp); | ||
78 | void idr_init(struct idr *idp); | 79 | void idr_init(struct idr *idp); |
@@ -346,6 +346,19 @@ void idr_remove(struct idr *idp, int id) | |||
346 | EXPORT_SYMBOL(idr_remove); | 346 | EXPORT_SYMBOL(idr_remove); |
347 | 347 | ||
348 | /** | 348 | /** |
349 | * idr_destroy - release all cached layers within an idr tree | ||
350 | * idp: idr handle | ||
351 | */ | ||
352 | void idr_destroy(struct idr *idp) | ||
353 | { | ||
354 | while (idp->id_free_cnt) { | ||
355 | struct idr_layer *p = alloc_layer(idp); | ||
356 | kmem_cache_free(idr_layer_cache, p); | ||
357 | } | ||
358 | } | ||
359 | EXPORT_SYMBOL(idr_destroy); | ||
360 | |||
361 | /** | ||
349 | * idr_find - return pointer for given id | 362 | * idr_find - return pointer for given id |
350 | * @idp: idr handle | 363 | * @idp: idr handle |
351 | * @id: lookup key | 364 | * @id: lookup key |