Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c708762b authored by Adrien Schildknecht's avatar Adrien Schildknecht Committed by Daniel Rosenberg
Browse files

ANDROID: Squashfs: refactor page_actor



This patch essentially does 3 things:
  1/ Always use an array of page to store the data instead of a mix of
     buffers and pages.
  2/ It is now possible to have 'holes' in a page actor, i.e. NULL
     pages in the array.
     When reading a block (default 128K), squashfs tries to grab all
     the pages covering this block. If a single page is up-to-date or
     locked, it falls back to using an intermediate buffer to do the
     read and then copy the pages in the actor. Allowing holes in the
     page actor remove the need for this intermediate buffer.
  3/ Refactor the wrappers to share code that deals with page actors.

Change-Id: I975801b32966b7ea930aebbb32d1b77233d2d2ce
Signed-off-by: default avatarAdrien Schildknecht <adriens@google.com>
parent f8715056
Loading
Loading
Loading
Loading
+29 −44
Original line number Diff line number Diff line
@@ -209,17 +209,14 @@ void squashfs_cache_put(struct squashfs_cache_entry *entry)
 */
void squashfs_cache_delete(struct squashfs_cache *cache)
{
	int i, j;
	int i;

	if (cache == NULL)
		return;

	for (i = 0; i < cache->entries; i++) {
		if (cache->entry[i].data) {
			for (j = 0; j < cache->pages; j++)
				kfree(cache->entry[i].data[j]);
			kfree(cache->entry[i].data);
		}
		if (cache->entry[i].page)
			free_page_array(cache->entry[i].page, cache->pages);
		kfree(cache->entry[i].actor);
	}

@@ -236,7 +233,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
struct squashfs_cache *squashfs_cache_init(char *name, int entries,
	int block_size)
{
	int i, j;
	int i;
	struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);

	if (cache == NULL) {
@@ -268,22 +265,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
		init_waitqueue_head(&cache->entry[i].wait_queue);
		entry->cache = cache;
		entry->block = SQUASHFS_INVALID_BLK;
		entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
		if (entry->data == NULL) {
		entry->page = alloc_page_array(cache->pages, GFP_KERNEL);
		if (!entry->page) {
			ERROR("Failed to allocate %s cache entry\n", name);
			goto cleanup;
		}

		for (j = 0; j < cache->pages; j++) {
			entry->data[j] = kmalloc(PAGE_SIZE, GFP_KERNEL);
			if (entry->data[j] == NULL) {
				ERROR("Failed to allocate %s buffer\n", name);
				goto cleanup;
			}
		}

		entry->actor = squashfs_page_actor_init(entry->data,
						cache->pages, 0);
		entry->actor = squashfs_page_actor_init(entry->page,
			cache->pages, 0, NULL);
		if (entry->actor == NULL) {
			ERROR("Failed to allocate %s cache entry\n", name);
			goto cleanup;
@@ -314,18 +302,20 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
		return min(length, entry->length - offset);

	while (offset < entry->length) {
		void *buff = entry->data[offset / PAGE_SIZE]
		void *buff = kmap_atomic(entry->page[offset / PAGE_SIZE])
			     + (offset % PAGE_SIZE);
		int bytes = min_t(int, entry->length - offset,
				PAGE_SIZE - (offset % PAGE_SIZE));

		if (bytes >= remaining) {
			memcpy(buffer, buff, remaining);
			kunmap_atomic(buff);
			remaining = 0;
			break;
		}

		memcpy(buffer, buff, bytes);
		kunmap_atomic(buff);
		buffer += bytes;
		remaining -= bytes;
		offset += bytes;
@@ -416,43 +406,38 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
void *squashfs_read_table(struct super_block *sb, u64 block, int length)
{
	int pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
	int i, res;
	void *table, *buffer, **data;
	struct page **page;
	void *buff;
	int res;
	struct squashfs_page_actor *actor;

	table = buffer = kmalloc(length, GFP_KERNEL);
	if (table == NULL)
	page = alloc_page_array(pages, GFP_KERNEL);
	if (!page)
		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);
	actor = squashfs_page_actor_init(page, pages, length, NULL);
	if (actor == NULL) {
		res = -ENOMEM;
		goto failed2;
		goto failed;
	}

	for (i = 0; i < pages; i++, buffer += PAGE_SIZE)
		data[i] = buffer;

	res = squashfs_read_data(sb, block, length |
		SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);

	kfree(data);
	kfree(actor);

	if (res < 0)
		goto failed;
		goto failed2;

	return table;
	buff = kmalloc(length, GFP_KERNEL);
	if (!buff)
		goto failed2;
	squashfs_actor_to_buf(actor, buff, length);
	squashfs_page_actor_free(actor, 0);
	free_page_array(page, pages);
	return buff;

failed2:
	kfree(data);
	squashfs_page_actor_free(actor, 0);
failed:
	kfree(table);
	free_page_array(page, pages);
	return ERR_PTR(res);
}
+30 −25
Original line number Diff line number Diff line
@@ -24,7 +24,8 @@
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
#include <linux/highmem.h>
#include <linux/fs.h>

#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -101,24 +102,26 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
static void *get_comp_opts(struct super_block *sb, unsigned short flags)
{
	struct squashfs_sb_info *msblk = sb->s_fs_info;
	void *buffer = NULL, *comp_opts;
	void *comp_opts, *buffer = NULL;
	struct page *page;
	struct squashfs_page_actor *actor = NULL;
	int length = 0;

	if (!SQUASHFS_COMP_OPTS(flags))
		return squashfs_comp_opts(msblk, buffer, length);

	/*
	 * Read decompressor specific options from file system if present
	 */
	if (SQUASHFS_COMP_OPTS(flags)) {
		buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
		if (buffer == NULL) {
			comp_opts = ERR_PTR(-ENOMEM);
			goto out;
		}

		actor = squashfs_page_actor_init(&buffer, 1, 0);
	page = alloc_page(GFP_KERNEL);
	if (!page)
		return ERR_PTR(-ENOMEM);

	actor = squashfs_page_actor_init(&page, 1, 0, NULL);
	if (actor == NULL) {
		comp_opts = ERR_PTR(-ENOMEM);
			goto out;
		goto actor_error;
	}

	length = squashfs_read_data(sb,
@@ -126,15 +129,17 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)

	if (length < 0) {
		comp_opts = ERR_PTR(length);
			goto out;
		}
		goto read_error;
	}

	buffer = kmap_atomic(page);
	comp_opts = squashfs_comp_opts(msblk, buffer, length);
	kunmap_atomic(buffer);

out:
	kfree(actor);
	kfree(buffer);
read_error:
	squashfs_page_actor_free(actor, 0);
actor_error:
	__free_page(page);
	return comp_opts;
}

+2 −2
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
	 * Create a "page actor" which will kmap and kunmap the
	 * page cache pages appropriately within the decompressor
	 */
	actor = squashfs_page_actor_init_special(page, pages, 0);
	actor = squashfs_page_actor_init(page, pages, 0, NULL);
	if (actor == NULL)
		goto out;

@@ -131,7 +131,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
	}

out:
	kfree(actor);
	squashfs_page_actor_free(actor, 0);
	kfree(page);
	return res;
}
+5 −26
Original line number Diff line number Diff line
@@ -94,39 +94,18 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
	struct buffer_head **bh, int b, int offset, int length,
	struct squashfs_page_actor *output)
{
	int res;
	size_t dest_len = output->length;
	struct squashfs_lz4 *stream = strm;
	void *buff = stream->input, *data;
	int avail, i, bytes = length, res;

	for (i = 0; i < b; i++) {
		avail = min(bytes, msblk->devblksize - offset);
		memcpy(buff, bh[i]->b_data + offset, avail);
		buff += avail;
		bytes -= avail;
		offset = 0;
		put_bh(bh[i]);
	}

	squashfs_bh_to_buf(bh, b, stream->input, offset, length,
		msblk->devblksize);
	res = LZ4_decompress_safe(stream->input, stream->output,
		length, output->length);

	if (res < 0)
		return -EIO;

	bytes = res;
	data = squashfs_first_page(output);
	buff = stream->output;
	while (data) {
		if (bytes <= PAGE_SIZE) {
			memcpy(data, buff, bytes);
			break;
		}
		memcpy(data, buff, PAGE_SIZE);
		buff += PAGE_SIZE;
		bytes -= PAGE_SIZE;
		data = squashfs_next_page(output);
	}
	squashfs_finish_page(output);
	squashfs_buf_to_actor(stream->output, output, res);

	return res;
}
+7 −33
Original line number Diff line number Diff line
@@ -79,45 +79,19 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
	struct buffer_head **bh, int b, int offset, int length,
	struct squashfs_page_actor *output)
{
	struct squashfs_lzo *stream = strm;
	void *buff = stream->input, *data;
	int avail, i, bytes = length, res;
	int res;
	size_t out_len = output->length;
	struct squashfs_lzo *stream = strm;

	for (i = 0; i < b; i++) {
		avail = min(bytes, msblk->devblksize - offset);
		memcpy(buff, bh[i]->b_data + offset, avail);
		buff += avail;
		bytes -= avail;
		offset = 0;
		put_bh(bh[i]);
	}

	squashfs_bh_to_buf(bh, b, stream->input, offset, length,
		msblk->devblksize);
	res = lzo1x_decompress_safe(stream->input, (size_t)length,
					stream->output, &out_len);
	if (res != LZO_E_OK)
		goto failed;

	res = bytes = (int)out_len;
	data = squashfs_first_page(output);
	buff = stream->output;
	while (data) {
		if (bytes <= PAGE_SIZE) {
			memcpy(data, buff, bytes);
			break;
		} else {
			memcpy(data, buff, PAGE_SIZE);
			buff += PAGE_SIZE;
			bytes -= PAGE_SIZE;
			data = squashfs_next_page(output);
		}
	}
	squashfs_finish_page(output);

	return res;

failed:
		return -EIO;
	squashfs_buf_to_actor(stream->output, output, out_len);

	return out_len;
}

const struct squashfs_decompressor squashfs_lzo_comp_ops = {
Loading