diff options
Diffstat (limited to 'fs/squashfs/cache.c')
| -rw-r--r-- | fs/squashfs/cache.c | 96 | 
1 files changed, 71 insertions, 25 deletions
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index 57314bee905..1cb70a0b216 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c @@ -2,7 +2,7 @@   * Squashfs - a compressed read only filesystem for Linux   *   * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher <phillip@lougher.demon.co.uk> + * Phillip Lougher <phillip@squashfs.org.uk>   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -29,7 +29,7 @@   * plus functions layered ontop of the generic cache implementation to   * access the metadata and fragment caches.   * - * To avoid out of memory and fragmentation isssues with vmalloc the cache + * To avoid out of memory and fragmentation issues with vmalloc the cache   * uses sequences of kmalloced PAGE_CACHE_SIZE buffers.   *   * It should be noted that the cache is not used for file datablocks, these @@ -55,8 +55,8 @@  #include "squashfs_fs.h"  #include "squashfs_fs_sb.h" -#include "squashfs_fs_i.h"  #include "squashfs.h" +#include "page_actor.h"  /*   * Look-up block in cache, and increment usage count.  If not in cache, read @@ -71,11 +71,15 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,  	spin_lock(&cache->lock);  	while (1) { -		for (i = 0; i < cache->entries; i++) -			if (cache->entry[i].block == block) +		for (i = cache->curr_blk, n = 0; n < cache->entries; n++) { +			if (cache->entry[i].block == block) { +				cache->curr_blk = i;  				break; +			} +			i = (i + 1) % cache->entries; +		} -		if (i == cache->entries) { +		if (n == cache->entries) {  			/*  			 * Block not in cache, if all cache entries are used  			 * go to sleep waiting for one to become available. @@ -105,7 +109,7 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,  			entry = &cache->entry[i];  			/* -			 * Initialise choosen cache entry, and fill it in from +			 * Initialise chosen cache entry, and fill it in from  			 * disk.  			 */  			cache->unused--; @@ -116,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,  			entry->error = 0;  			spin_unlock(&cache->lock); -			entry->length = squashfs_read_data(sb, entry->data, -				block, length, &entry->next_index, -				cache->block_size, cache->pages); +			entry->length = squashfs_read_data(sb, block, length, +				&entry->next_index, entry->actor);  			spin_lock(&cache->lock); @@ -217,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)  				kfree(cache->entry[i].data[j]);  			kfree(cache->entry[i].data);  		} +		kfree(cache->entry[i].actor);  	}  	kfree(cache->entry); @@ -246,6 +250,7 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,  		goto cleanup;  	} +	cache->curr_blk = 0;  	cache->next_blk = 0;  	cache->unused = entries;  	cache->entries = entries; @@ -276,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,  				goto cleanup;  			}  		} + +		entry->actor = squashfs_page_actor_init(entry->data, +						cache->pages, 0); +		if (entry->actor == NULL) { +			ERROR("Failed to allocate %s cache entry\n", name); +			goto cleanup; +		}  	}  	return cache; @@ -287,7 +299,7 @@ cleanup:  /* - * Copy upto length bytes from cache entry to buffer starting at offset bytes + * Copy up to length bytes from cache entry to buffer starting at offset bytes   * into the cache entry.  If there's not length bytes then copy the number of   * bytes available.  In all cases return the number of bytes copied.   */ @@ -333,17 +345,20 @@ int squashfs_read_metadata(struct super_block *sb, void *buffer,  		u64 *block, int *offset, int length)  {  	struct squashfs_sb_info *msblk = sb->s_fs_info; -	int bytes, copied = length; +	int bytes, res = length;  	struct squashfs_cache_entry *entry;  	TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset);  	while (length) {  		entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0); -		if (entry->error) -			return entry->error; -		else if (*offset >= entry->length) -			return -EIO; +		if (entry->error) { +			res = entry->error; +			goto error; +		} else if (*offset >= entry->length) { +			res = -EIO; +			goto error; +		}  		bytes = squashfs_copy_data(buffer, entry, *offset, length);  		if (buffer) @@ -359,7 +374,11 @@ int squashfs_read_metadata(struct super_block *sb, void *buffer,  		squashfs_cache_put(entry);  	} -	return copied; +	return res; + +error: +	squashfs_cache_put(entry); +	return res;  } @@ -394,19 +413,46 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,  /*   * Read a filesystem table (uncompressed sequence of bytes) from disk   */ -int squashfs_read_table(struct super_block *sb, void *buffer, u64 block, -	int length) +void *squashfs_read_table(struct super_block *sb, u64 block, int length)  {  	int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;  	int i, res; -	void **data = kcalloc(pages, sizeof(void *), GFP_KERNEL); -	if (data == NULL) -		return -ENOMEM; +	void *table, *buffer, **data; +	struct squashfs_page_actor *actor; + +	table = buffer = kmalloc(length, GFP_KERNEL); +	if (table == NULL) +		return ERR_PTR(-ENOMEM); + +	data = kcalloc(pages, sizeof(void *), GFP_KERNEL); +	if (data == NULL) { +		res = -ENOMEM; +		goto failed; +	} + +	actor = squashfs_page_actor_init(data, pages, length); +	if (actor == NULL) { +		res = -ENOMEM; +		goto failed2; +	}  	for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)  		data[i] = buffer; -	res = squashfs_read_data(sb, data, block, length | -		SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages); + +	res = squashfs_read_data(sb, block, length | +		SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor); +  	kfree(data); -	return res; +	kfree(actor); + +	if (res < 0) +		goto failed; + +	return table; + +failed2: +	kfree(data); +failed: +	kfree(table); +	return ERR_PTR(res);  }  | 
