858 lines · 24.1 KB · C
RawDownload
/* * linux/fs/mbcache.c * (C) 2001-2002 Andreas Gruenbacher, <a.gruenbacher@computer.org> */ /* * Filesystem Meta Information Block Cache (mbcache) * * The mbcache caches blocks of block devices that need to be located * by their device/block number, as well as by other criteria (such * as the block's contents). * * There can only be one cache entry in a cache per device and block number. * Additional indexes need not be unique in this sense. The number of * additional indexes (=other criteria) can be hardwired at compile time * or specified at cache create time. * * Each cache entry is of fixed size. An entry may be `valid' or `invalid' * in the cache. A valid entry is in the main hash tables of the cache, * and may also be in the lru list. An invalid entry is not in any hashes * or lists. * * A valid cache entry is only in the lru list if no handles refer to it. * Invalid cache entries will be freed when the last handle to the cache * entry is released. Entries that cannot be freed immediately are put * back on the lru list. */ /* * Lock descriptions and usage: * * Each hash chain of both the block and index hash tables now contains * a built-in lock used to serialize accesses to the hash chain. * * Accesses to global data structures mb_cache_list and mb_cache_lru_list * are serialized via the global spinlock mb_cache_spinlock. * * Each mb_cache_entry contains a spinlock, e_entry_lock, to serialize * accesses to its local data, such as e_used and e_queued. * * Lock ordering: * * Each block hash chain's lock has the highest lock order, followed by an * index hash chain's lock, mb_cache_bg_lock (used to implement mb_cache_entry's * lock), and mb_cach_spinlock, with the lowest order.  While holding * either a block or index hash chain lock, a thread can acquire an * mc_cache_bg_lock, which in turn can also acquire mb_cache_spinlock. * * Synchronization: * * Since both mb_cache_entry_get and mb_cache_entry_find scan the block and * index hash chian, it needs to lock the corresponding hash chain.  For each * mb_cache_entry within the chain, it needs to lock the mb_cache_entry to * prevent either any simultaneous release or free on the entry and also * to serialize accesses to either the e_used or e_queued member of the entry. * * To avoid having a dangling reference to an already freed * mb_cache_entry, an mb_cache_entry is only freed when it is not on a * block hash chain and also no longer being referenced, both e_used, * and e_queued are 0's.  When an mb_cache_entry is explicitly freed it is * first removed from a block hash chain. */ #include <linux/kernel.h>#include <linux/module.h> #include <linux/hash.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/list_bl.h>#include <linux/mbcache.h>#include <linux/init.h>#include <linux/blockgroup_lock.h>#include <linux/log2.h> #ifdef MB_CACHE_DEBUG# define mb_debug(f...) do { \		printk(KERN_DEBUG f); \		printk("\n"); \	} while (0)#define mb_assert(c) do { if (!(c)) \		printk(KERN_ERR "assertion " #c " failed\n"); \	} while(0)#else# define mb_debug(f...) do { } while(0)# define mb_assert(c) do { } while(0)#endif#define mb_error(f...) do { \		printk(KERN_ERR f); \		printk("\n"); \	} while(0) #define MB_CACHE_WRITER ((unsigned short)~0U >> 1) #define MB_CACHE_ENTRY_LOCK_BITS	ilog2(NR_BG_LOCKS)#define	MB_CACHE_ENTRY_LOCK_INDEX(ce)			\	(hash_long((unsigned long)ce, MB_CACHE_ENTRY_LOCK_BITS)) static DECLARE_WAIT_QUEUE_HEAD(mb_cache_queue);static struct blockgroup_lock *mb_cache_bg_lock;static struct kmem_cache *mb_cache_kmem_cache; MODULE_AUTHOR("Andreas Gruenbacher <a.gruenbacher@computer.org>");MODULE_DESCRIPTION("Meta block cache (for extended attributes)");MODULE_LICENSE("GPL"); EXPORT_SYMBOL(mb_cache_create);EXPORT_SYMBOL(mb_cache_shrink);EXPORT_SYMBOL(mb_cache_destroy);EXPORT_SYMBOL(mb_cache_entry_alloc);EXPORT_SYMBOL(mb_cache_entry_insert);EXPORT_SYMBOL(mb_cache_entry_release);EXPORT_SYMBOL(mb_cache_entry_free);EXPORT_SYMBOL(mb_cache_entry_get);#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)EXPORT_SYMBOL(mb_cache_entry_find_first);EXPORT_SYMBOL(mb_cache_entry_find_next);#endif /* * Global data: list of all mbcache's, lru list, and a spinlock for * accessing cache data structures on SMP machines. The lru list is * global across all mbcaches. */ static LIST_HEAD(mb_cache_list);static LIST_HEAD(mb_cache_lru_list);static DEFINE_SPINLOCK(mb_cache_spinlock); static inline void__spin_lock_mb_cache_entry(struct mb_cache_entry *ce){	spin_lock(bgl_lock_ptr(mb_cache_bg_lock,		MB_CACHE_ENTRY_LOCK_INDEX(ce)));} static inline void__spin_unlock_mb_cache_entry(struct mb_cache_entry *ce){	spin_unlock(bgl_lock_ptr(mb_cache_bg_lock,		MB_CACHE_ENTRY_LOCK_INDEX(ce)));} static inline int__mb_cache_entry_is_block_hashed(struct mb_cache_entry *ce){	return !hlist_bl_unhashed(&ce->e_block_list);}  static inline void__mb_cache_entry_unhash_block(struct mb_cache_entry *ce){	if (__mb_cache_entry_is_block_hashed(ce))		hlist_bl_del_init(&ce->e_block_list);} static inline int__mb_cache_entry_is_index_hashed(struct mb_cache_entry *ce){	return !hlist_bl_unhashed(&ce->e_index.o_list);} static inline void__mb_cache_entry_unhash_index(struct mb_cache_entry *ce){	if (__mb_cache_entry_is_index_hashed(ce))		hlist_bl_del_init(&ce->e_index.o_list);} /* * __mb_cache_entry_unhash_unlock() * * This function is called to unhash both the block and index hash * chain. * It assumes both the block and index hash chain is locked upon entry. * It also unlock both hash chains both exit */static inline void__mb_cache_entry_unhash_unlock(struct mb_cache_entry *ce){	__mb_cache_entry_unhash_index(ce);	hlist_bl_unlock(ce->e_index_hash_p);	__mb_cache_entry_unhash_block(ce);	hlist_bl_unlock(ce->e_block_hash_p);} static void__mb_cache_entry_forget(struct mb_cache_entry *ce, gfp_t gfp_mask){	struct mb_cache *cache = ce->e_cache; 	mb_assert(!(ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt)));	kmem_cache_free(cache->c_entry_cache, ce);	atomic_dec(&cache->c_entry_count);} static void__mb_cache_entry_release(struct mb_cache_entry *ce){	/* First lock the entry to serialize access to its local data. */	__spin_lock_mb_cache_entry(ce);	/* Wake up all processes queuing for this cache entry. */	if (ce->e_queued)		wake_up_all(&mb_cache_queue);	if (ce->e_used >= MB_CACHE_WRITER)		ce->e_used -= MB_CACHE_WRITER;	/*	 * Make sure that all cache entries on lru_list have	 * both e_used and e_qued of 0s.	 */	ce->e_used--;	if (!(ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt))) {		if (!__mb_cache_entry_is_block_hashed(ce)) {			__spin_unlock_mb_cache_entry(ce);			goto forget;		}		/*		 * Need access to lru list, first drop entry lock,		 * then reacquire the lock in the proper order.		 */		spin_lock(&mb_cache_spinlock);		if (list_empty(&ce->e_lru_list))			list_add_tail(&ce->e_lru_list, &mb_cache_lru_list);		spin_unlock(&mb_cache_spinlock);	}	__spin_unlock_mb_cache_entry(ce);	return;forget:	mb_assert(list_empty(&ce->e_lru_list));	__mb_cache_entry_forget(ce, GFP_KERNEL);} /* * mb_cache_shrink_scan()  memory pressure callback * * This function is called by the kernel memory management when memory * gets low. * * @shrink: (ignored) * @sc: shrink_control passed from reclaim * * Returns the number of objects freed. */static unsigned longmb_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc){	LIST_HEAD(free_list);	struct mb_cache_entry *entry, *tmp;	int nr_to_scan = sc->nr_to_scan;	gfp_t gfp_mask = sc->gfp_mask;	unsigned long freed = 0; 	mb_debug("trying to free %d entries", nr_to_scan);	spin_lock(&mb_cache_spinlock);	while ((nr_to_scan-- > 0) && !list_empty(&mb_cache_lru_list)) {		struct mb_cache_entry *ce =			list_entry(mb_cache_lru_list.next,				struct mb_cache_entry, e_lru_list);		list_del_init(&ce->e_lru_list);		if (ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt))			continue;		spin_unlock(&mb_cache_spinlock);		/* Prevent any find or get operation on the entry */		hlist_bl_lock(ce->e_block_hash_p);		hlist_bl_lock(ce->e_index_hash_p);		/* Ignore if it is touched by a find/get */		if (ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt) ||			!list_empty(&ce->e_lru_list)) {			hlist_bl_unlock(ce->e_index_hash_p);			hlist_bl_unlock(ce->e_block_hash_p);			spin_lock(&mb_cache_spinlock);			continue;		}		__mb_cache_entry_unhash_unlock(ce);		list_add_tail(&ce->e_lru_list, &free_list);		spin_lock(&mb_cache_spinlock);	}	spin_unlock(&mb_cache_spinlock); 	list_for_each_entry_safe(entry, tmp, &free_list, e_lru_list) {		__mb_cache_entry_forget(entry, gfp_mask);		freed++;	}	return freed;} static unsigned longmb_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc){	struct mb_cache *cache;	unsigned long count = 0; 	spin_lock(&mb_cache_spinlock);	list_for_each_entry(cache, &mb_cache_list, c_cache_list) {		mb_debug("cache %s (%d)", cache->c_name,			  atomic_read(&cache->c_entry_count));		count += atomic_read(&cache->c_entry_count);	}	spin_unlock(&mb_cache_spinlock); 	return vfs_pressure_ratio(count);} static struct shrinker mb_cache_shrinker = {	.count_objects = mb_cache_shrink_count,	.scan_objects = mb_cache_shrink_scan,	.seeks = DEFAULT_SEEKS,}; /* * mb_cache_create()  create a new cache * * All entries in one cache are equal size. Cache entries may be from * multiple devices. If this is the first mbcache created, registers * the cache with kernel memory management. Returns NULL if no more * memory was available. * * @name: name of the cache (informal) * @bucket_bits: log2(number of hash buckets) */struct mb_cache *mb_cache_create(const char *name, int bucket_bits){	int n, bucket_count = 1 << bucket_bits;	struct mb_cache *cache = NULL; 	if (!mb_cache_bg_lock) {		mb_cache_bg_lock = kmalloc(sizeof(struct blockgroup_lock),			GFP_KERNEL);		if (!mb_cache_bg_lock)			return NULL;		bgl_lock_init(mb_cache_bg_lock);	} 	cache = kmalloc(sizeof(struct mb_cache), GFP_KERNEL);	if (!cache)		return NULL;	cache->c_name = name;	atomic_set(&cache->c_entry_count, 0);	cache->c_bucket_bits = bucket_bits;	cache->c_block_hash = kmalloc(bucket_count *		sizeof(struct hlist_bl_head), GFP_KERNEL);	if (!cache->c_block_hash)		goto fail;	for (n=0; n<bucket_count; n++)		INIT_HLIST_BL_HEAD(&cache->c_block_hash[n]);	cache->c_index_hash = kmalloc(bucket_count *		sizeof(struct hlist_bl_head), GFP_KERNEL);	if (!cache->c_index_hash)		goto fail;	for (n=0; n<bucket_count; n++)		INIT_HLIST_BL_HEAD(&cache->c_index_hash[n]);	if (!mb_cache_kmem_cache) {		mb_cache_kmem_cache = kmem_cache_create(name,			sizeof(struct mb_cache_entry), 0,			SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL);		if (!mb_cache_kmem_cache)			goto fail2;	}	cache->c_entry_cache = mb_cache_kmem_cache; 	/*	 * Set an upper limit on the number of cache entries so that the hash	 * chains won't grow too long.	 */	cache->c_max_entries = bucket_count << 4; 	spin_lock(&mb_cache_spinlock);	list_add(&cache->c_cache_list, &mb_cache_list);	spin_unlock(&mb_cache_spinlock);	return cache; fail2:	kfree(cache->c_index_hash); fail:	kfree(cache->c_block_hash);	kfree(cache);	return NULL;}  /* * mb_cache_shrink() * * Removes all cache entries of a device from the cache. All cache entries * currently in use cannot be freed, and thus remain in the cache. All others * are freed. * * @bdev: which device's cache entries to shrink */voidmb_cache_shrink(struct block_device *bdev){	LIST_HEAD(free_list);	struct list_head *l;	struct mb_cache_entry *ce, *tmp; 	l = &mb_cache_lru_list;	spin_lock(&mb_cache_spinlock);	while (!list_is_last(l, &mb_cache_lru_list)) {		l = l->next;		ce = list_entry(l, struct mb_cache_entry, e_lru_list);		if (ce->e_bdev == bdev) {			list_del_init(&ce->e_lru_list);			if (ce->e_used || ce->e_queued ||				atomic_read(&ce->e_refcnt))				continue;			spin_unlock(&mb_cache_spinlock);			/*			 * Prevent any find or get operation on the entry.			 */			hlist_bl_lock(ce->e_block_hash_p);			hlist_bl_lock(ce->e_index_hash_p);			/* Ignore if it is touched by a find/get */			if (ce->e_used || ce->e_queued ||				atomic_read(&ce->e_refcnt) ||				!list_empty(&ce->e_lru_list)) {				hlist_bl_unlock(ce->e_index_hash_p);				hlist_bl_unlock(ce->e_block_hash_p);				l = &mb_cache_lru_list;				spin_lock(&mb_cache_spinlock);				continue;			}			__mb_cache_entry_unhash_unlock(ce);			mb_assert(!(ce->e_used || ce->e_queued ||				atomic_read(&ce->e_refcnt)));			list_add_tail(&ce->e_lru_list, &free_list);			l = &mb_cache_lru_list;			spin_lock(&mb_cache_spinlock);		}	}	spin_unlock(&mb_cache_spinlock); 	list_for_each_entry_safe(ce, tmp, &free_list, e_lru_list) {		__mb_cache_entry_forget(ce, GFP_KERNEL);	}}  /* * mb_cache_destroy() * * Shrinks the cache to its minimum possible size (hopefully 0 entries), * and then destroys it. If this was the last mbcache, un-registers the * mbcache from kernel memory management. */voidmb_cache_destroy(struct mb_cache *cache){	LIST_HEAD(free_list);	struct mb_cache_entry *ce, *tmp; 	spin_lock(&mb_cache_spinlock);	list_for_each_entry_safe(ce, tmp, &mb_cache_lru_list, e_lru_list) {		if (ce->e_cache == cache)			list_move_tail(&ce->e_lru_list, &free_list);	}	list_del(&cache->c_cache_list);	spin_unlock(&mb_cache_spinlock); 	list_for_each_entry_safe(ce, tmp, &free_list, e_lru_list) {		list_del_init(&ce->e_lru_list);		/*		 * Prevent any find or get operation on the entry.		 */		hlist_bl_lock(ce->e_block_hash_p);		hlist_bl_lock(ce->e_index_hash_p);		mb_assert(!(ce->e_used || ce->e_queued ||			atomic_read(&ce->e_refcnt)));		__mb_cache_entry_unhash_unlock(ce);		__mb_cache_entry_forget(ce, GFP_KERNEL);	} 	if (atomic_read(&cache->c_entry_count) > 0) {		mb_error("cache %s: %d orphaned entries",			  cache->c_name,			  atomic_read(&cache->c_entry_count));	} 	if (list_empty(&mb_cache_list)) {		kmem_cache_destroy(mb_cache_kmem_cache);		mb_cache_kmem_cache = NULL;	}	kfree(cache->c_index_hash);	kfree(cache->c_block_hash);	kfree(cache);} /* * mb_cache_entry_alloc() * * Allocates a new cache entry. The new entry will not be valid initially, * and thus cannot be looked up yet. It should be filled with data, and * then inserted into the cache using mb_cache_entry_insert(). Returns NULL * if no more memory was available. */

Lines 1–500 of 858.